Overview

Configure a chart once. Ship it as your own code.

Graphite is a chart library for Next.js and React. You design the look in the app — colors, fonts, shape, light/dark, surface — define the data shape your component accepts, and download a small set of TypeScript files that live in your project. There is no runtime dependency on this app.

Your code, not a dependency
You download plain TypeScript files into your repo. Edit them freely; nothing phones home.
Data in, chart out
Components are presentational. Fetch anywhere — server, hook, static — and pass rows as a prop.
One theme, every chart
Palette, font, shape, light/dark and surface live in one typed theme file.
Quick start

From zero to a chart in five minutes

1
Design it
Open the app, pick a chart type in the strip and tune colors, font, shape and mode until the preview looks right.
2
Name your fields
In Data, set the x field and value/series fields to the keys your API returns. Paste a few real rows to validate.
3
Download library
Press Download library. Unzip at your project root.
unzip graphite-line-chart.zip -d ./
4
Render it
Import the component and pass rows in.
import { LineChart } from "@/components/charts/LineChart";

<LineChart data={rows} />
Using the app

Three steps, top to bottom

1
Pick a chart type
The strip under the header lists 12 primitives: line, area, bar, stacked, donut, scatter, radar, heatmap, sparkline, funnel, gauge and treemap. Switching resets the sample data to a matching shape.
2
Design
Chart mode (auto / light / dark), surface (solid / glass / none), palette or four custom colors, font, corner radius, stroke width, fill intensity, curve style, and toggles for gridlines, legend and value labels. The preview updates live.
3
Data
Choose a sample dataset for realistic placeholder values, then name the x field and the value or series fields your rows will carry. Paste your own JSON to check the shape — errors show inline and the "Resulting usage" box shows how the component will be called.
4
Code
Read the generated files, copy a single one, or press Download library for a zip with the full folder structure. Your choices are saved in this browser, so you can come back and tweak.

Tips: hover any chart for a tooltip, use ← → to step through points and Esc to clear. The sun/moon button switches the app; the chart's own mode is set under Design → Chart mode.

Installation

Add the downloaded files to your project

The zip mirrors a standard App Router layout. Unzip it at the root of your Next.js project; existing folders are merged, nothing else is touched.

your-app/
├─ app/
│  ├─ globals.css                 ← Graphite tokens (append to your existing file)
│  └─ dashboard/page.tsx          ← example usage (optional)
├─ components/charts/
│  ├─ LineChart.tsx               ← the component
│  ├─ LineChart.types.ts          ← schema + props contract
│  ├─ LiveLineChart.tsx           ← SWR polling example (optional)
│  └─ graphite/Chart.tsx          ← the SVG renderer (React only)
└─ lib/
   └─ graphite.theme.ts           ← your design choices as a typed theme
  1. 1Unzip into your project root. Merge globals.css into your existing app/globals.css if you already have one.
  2. 2Make sure zod is available (used to validate rows) — and swr only if you keep the Live client example.
  3. 3Set data-theme="light" or "dark" on <html>, or leave it off to follow the user's system preference.
npm install zod        # required
npm install swr        # only for LiveLineChart.tsx
// app/layout.tsx — light/dark is driven by a data attribute on <html>
export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" data-theme="dark" suppressHydrationWarning>
      <body>{children}</body>
    </html>
  );
}
MCP server

Generate charts from Claude Code

Graphite includes a Model Context Protocol server, so Claude Code (or any MCP client) can generate charts straight into the project you are working on. It runs inside the same app — including the Docker container — at /mcp over Streamable HTTP. It is open: no API key, no login.

Endpointhttp://localhost:6786/mcp

1. Start Graphite

docker compose up -d --build   # serves the app and the MCP server on :6786

Or run npm run dev for development (the endpoint is then http://localhost:3000/mcp). Opening the endpoint in a browser shows a short JSON description, which is a quick way to check it is reachable.

2. Add it to Claude Code

For you, in every project
claude mcp add --transport http --scope user graphite http://localhost:6786/mcp
For one repo, shared with the team
claude mcp add --transport http --scope project graphite http://localhost:6786/mcp

The project scope writes a .mcp.json at the repo root that you can commit. You can also create it by hand:

{
  "mcpServers": {
    "graphite": {
      "type": "http",
      "url": "http://localhost:6786/mcp"
    }
  }
}

3. Check the connection

claude mcp list
# graphite: http://localhost:6786/mcp (HTTP) - ✔ Connected

A project-scoped server shows Pending approval until you start claude in that repo and approve it. Inside a session, /mcp lists the server and its tools. Claude asks the first time it calls a Graphite tool; allow it for the session or always. If it shows as failed, open http://localhost:6786/mcp in a browser — you should see a short JSON description.

Tools

list_chart_typesThe 12 chart types, the row shape each expects, default field names and an example row.
get_design_optionsPalettes (with colors), fonts, surfaces, curves, modes, sample scenarios and value ranges.
validate_dataChecks real rows against a chart type, infers the x and value fields, flags missing fields or wrong types and returns the zod schema.
generate_chartReturns the component, types, theme, CSS tokens and renderer as files to write — plus a live preview link and a link that opens the chart in the studio.
generate_themeOnly lib/graphite.theme.ts and the CSS tokens — restyle every chart in a project at once.
get_chart_runtimeThe shared SVG renderer, components/charts/graphite/Chart.tsx.

generate_chart accepts the same choices as the studio: type, data (example rows), x, keys, title, unit, componentName, palette (an id or four hex colors), font, radius, stroke, fill, curve, grid, legend, labels, mode, surface — and files to choose which files come back. By default you get the component, types, theme, CSS tokens and renderer; add usage and live for the example page and SWR client. Leave out runtime and theme once a project already has them.

Things to ask

Use graphite to add a line chart of p95 latency per region to the dashboard page. Rows come from /api/latency.
Call validate_data with a sample of what getRevenue() returns, then generate a bar chart called RevenueChart with the ember palette.
Generate a donut chart for plan distribution with a glass surface; skip the runtime and theme files, they already exist.
Restyle all Graphite charts in this repo: generate_theme with the ocean palette, Sora font and radius 12.

Other clients and remote hosts

  • Cursor, Windsurf, VS Code and other HTTP-capable clients: use the same { "type": "http", "url": "http://localhost:6786/mcp" } entry in their MCP config.
  • Clients that only speak stdio (for example Claude Desktop's config file): bridge with npx -y mcp-remote http://localhost:6786/mcp as the command.
  • Graphite on another machine: use that host's address instead of localhost. If it sits behind a reverse proxy or a different public URL, set PUBLIC_URL (for example https://charts.example.com) in docker-compose.yml so preview and studio links point to the right place.
  • No authentication: anyone who can reach the port can call the tools. They only generate code and never read or write files on the server, but keep the port on localhost or a trusted network.
Passing data

The component never fetches

Every chart takes a data prop: an array of row objects whose field names you chose in the Data step. Where the rows come from is up to you.

Server Component
async function Page() {
  const res = await fetch(API, {
    next: { revalidate: 60 },
  });
  const rows = LineDatumSchema
    .array()
    .parse(await res.json());
  return <LineChart data={rows} />;
}
Client hook
"use client";
function Live() {
  const { data, isLoading } =
    useSWR<LineDatum[]>(url, fetcher);
  return (
    <LineChart
      data={data ?? []}
      loading={isLoading}
    />
  );
}

Rows are validated at the boundary with the generated zod schema, so a shape mismatch fails loudly in development instead of rendering an empty chart.

Chart types

What each chart expects per row

Field names are yours to choose in the Data step; the shapes below are the defaults the app starts from.

TypeFieldsExample row
Linelabel · series…{ "label": "08:00", "eu_west_1": 140, "us_east_1": 111 }
Arealabel · series…{ "label": "Mon", "grid": 24, "solar": 18 }
Barlabel · value{ "label": "Edge", "value": 312 }
Stackedlabel · series…{ "label": "Q1", "paid": 20, "social": 15, "organic": 10 }
Donutname · value{ "name": "Compute", "value": 38 }
Scatterx · value{ "x": 212, "value": 64 }
Radaraxis · series…{ "axis": "Latency", "now": 82, "prev": 65 }
Heatmapday · hours[]{ "day": "Mon", "hours": [3, 5, 8, …24 numbers] }
Sparklinelabel · values[]{ "label": "BTC", "values": [41.2, 43.8, 42.1, …] }
Funnelname · value{ "name": "Requests", "value": 12400 }
Gaugename · value (0–100){ "name": "CPU load", "value": 68 }
Treemapname · value{ "name": "Storage", "value": 3570 }
Props

Every generated component accepts

datareadonly Datum[]Rows to render. Field names match what you defined in the Data step.
loadingbooleanRenders a skeleton in place of the chart.
emptyStateReactNodeShown when data is empty and not loading.
onPointClick(d: Datum) => voidFires with the row under the cursor (click, or Enter while stepping with the keyboard).
formatValue(v: number) => stringCustom number formatting for axes, labels and tooltips.
mode / surface"auto" | … / "solid" | …Override the theme defaults per instance.
classNamestringMerged onto the root element.
Theming

Light, dark and glass

graphite.theme.ts holds your palette, font, radius, stroke, fill and both mode token sets. Import it once and pass it to every chart; change it in one place to restyle them all.

mode="auto"
Reads <html data-theme>; falls back to prefers-color-scheme. Default from the app.
surface="glass"
Translucent card with backdrop blur — sits on gradients and imagery.
surface="transparent"
No card at all; the chart inherits whatever is behind it.
<LineChart data={rows} mode="auto" surface="glass" />
// mode:    "light" | "dark" | "auto"  — auto follows <html data-theme> then prefers-color-scheme
// surface: "solid" | "glass" | "transparent"
FAQ
Does it work with the Pages Router?
Yes. The component is a normal client component; only the page.tsx example uses App Router conventions.
Can I change the design later?
Reopen the app, adjust, and download again — or edit graphite.theme.ts by hand. Both are plain files.
Are the charts accessible?
Each chart renders with role="img" and an aria-label from its title; tooltips are keyboard reachable with arrow keys, and Esc clears them.
How big is it?
A handful of files and SVG rendering. The renderer in graphite/Chart.tsx depends only on React — there is no charting runtime to bundle.
Where does the sample data come from?
It is generated in the app for preview only. Nothing from it is written into your component — only the field names you chose (plus three placeholder rows in the example page).