A React data visualization library designed for AI-assisted development.
Simple charts in 5 lines. Network graphs, streaming data, and coordinated dashboards when you need them. Structured schemas and an MCP server so AI coding assistants generate correct chart code on the first try.
import { LineChart } from "semiotic"
<LineChart
data={salesData}
xAccessor="month"
yAccessor="revenue"
/>Semiotic is a data visualization library for React that combines broad chart coverage with first-class AI tooling. It handles the chart types that most libraries skip — network graphs, streaming data, statistical distributions, coordinated views — and ships with machine-readable schemas so LLMs can generate correct code without examples.
Semiotic ships with everything an AI coding assistant needs to generate correct visualizations without trial and error:
semiotic/ai— a single import with all 28 chart components, optimized for LLM code generationai/schema.json— machine-readable prop schemas for every componentnpx semiotic-mcp— an MCP server for tool-based chart rendering in any MCP clientnpx semiotic-ai --doctor— validate component + props JSON from the command lineCLAUDE.md— instruction files auto-synced for Claude, Cursor, Copilot, Windsurf, and Clinellms.txt— machine-readable documentation following the emerging standard
Every chart includes a built-in error boundary and dev-mode validation warnings with typo suggestions, so AI-generated code fails gracefully with actionable diagnostics instead of a blank screen.
Network visualization. Force-directed graphs, Sankey diagrams, chord diagrams, tree layouts, treemaps, circle packing, and orbit diagrams — all as React components with the same prop API as LineChart.
Streaming data. Realtime charts render on canvas at 60fps with a ref-based push API. Built-in decay, pulse, and staleness encoding for monitoring dashboards.
Coordinated views. LinkedCharts provides hover cross-highlighting,
brush cross-filtering, and selection synchronization across any combination
of chart types — zero wiring.
Statistical summaries. Box plots, violin plots, swarm plots, histograms, LOESS smoothing, forecast with confidence envelopes, and anomaly detection. Marginal distribution graphics on scatterplot axes with a single prop.
| Layer | For | Example |
|---|---|---|
| Charts | Common visualizations with sensible defaults | <LineChart data={d} xAccessor="x" yAccessor="y" /> |
| Frames | Full control over rendering, interaction, and layout | <StreamXYFrame chartType="line" lineStyle={...} /> |
Every Chart component accepts a frameProps prop to access the underlying
Frame API without leaving the simpler interface.
Charts serialize to JSON and back: toConfig, fromConfig, toURL,
copyConfig, configToJSX. Have Vega-Lite specs? fromVegaLite(spec)
translates them to Semiotic configs — works with configToJSX() for
full round-trip from notebooks and AI-generated specs.
Need a standard bar or line chart for a dashboard you'll never need to customize beyond colors and labels? Recharts has a larger ecosystem and more community examples. Need GPU-accelerated rendering for millions of data points? Apache ECharts handles that scale.
Semiotic is for projects that outgrow those libraries — when you need network graphs alongside time series, streaming data alongside static snapshots, or coordinated views across chart types.
npm install semioticRequires React 18.1+ or React 19.
Hover one chart, highlight the same data in another — zero wiring:
import { LinkedCharts, Scatterplot, BarChart } from "semiotic"
<LinkedCharts>
<Scatterplot
data={data} xAccessor="age" yAccessor="income" colorBy="region"
linkedHover={{ name: "hl", fields: ["region"] }}
selection={{ name: "hl" }}
/>
<BarChart
data={summary} categoryAccessor="region" valueAccessor="total"
selection={{ name: "hl" }}
/>
</LinkedCharts>Live data fades old points, flashes new ones, flags stale feeds:
import { RealtimeLineChart } from "semiotic"
const chartRef = useRef()
chartRef.current.push({ time: Date.now(), value: cpuLoad })
<RealtimeLineChart
ref={chartRef}
timeAccessor="time"
valueAccessor="value"
decay={{ type: "exponential", halfLife: 100 }}
staleness={{ threshold: 5000, showBadge: true }}
/>Force-directed graphs and Sankey diagrams — same API as LineChart:
import { ForceDirectedGraph, SankeyDiagram } from "semiotic"
<ForceDirectedGraph
nodes={people} edges={friendships}
colorBy="team" nodeSize={8} showLabels
/>
<SankeyDiagram
edges={budgetFlows}
sourceAccessor="from" targetAccessor="to" valueAccessor="amount"
/>Live service topology with threshold alerting and click-to-inspect:
import { StreamNetworkFrame, ChartContainer, DetailsPanel, LinkedCharts } from "semiotic"
const chartRef = useRef()
chartRef.current.push({ source: "API", target: "Orders", value: 15 })
<LinkedCharts>
<ChartContainer title="System Monitor" status="live"
detailsPanel={
<DetailsPanel position="right" trigger="click">
{(datum) => <div>{datum.id}: {datum.value} req/s</div>}
</DetailsPanel>
}>
<StreamNetworkFrame ref={chartRef} chartType="sankey"
showParticles particleStyle={{ proportionalSpeed: true }}
thresholds={{ metric: n => n.value, warning: 100, critical: 250 }}
/>
</ChartContainer>
</LinkedCharts>Line, bar, scatter, area — all the basics, with sensible defaults:
import { LineChart, BarChart } from "semiotic"
<LineChart
data={salesData}
xAccessor="month" yAccessor="revenue"
curve="monotoneX" showPoints
/>
<BarChart
data={categoryData}
categoryAccessor="department" valueAccessor="sales"
orientation="horizontal" colorBy="region"
/>| Category | Components |
|---|---|
| XY | LineChart AreaChart StackedAreaChart Scatterplot ConnectedScatterplot BubbleChart Heatmap |
| Categorical | BarChart StackedBarChart GroupedBarChart SwarmPlot BoxPlot Histogram ViolinPlot DotPlot PieChart DonutChart |
| Network | ForceDirectedGraph ChordDiagram SankeyDiagram TreeDiagram Treemap CirclePack OrbitDiagram |
| Realtime | RealtimeLineChart RealtimeHistogram RealtimeSwarmChart RealtimeWaterfallChart RealtimeHeatmap |
| Coordination | LinkedCharts ScatterplotMatrix |
| Layout | ChartGrid ContextLayout CategoryColorProvider |
| Frames | StreamXYFrame StreamOrdinalFrame StreamNetworkFrame |
Paste a Vega-Lite spec, get a Semiotic chart:
import { fromVegaLite } from "semiotic/data"
import { configToJSX, fromConfig } from "semiotic"
const config = fromVegaLite({
mark: "bar",
data: { values: [{ a: "A", b: 28 }, { a: "B", b: 55 }] },
encoding: {
x: { field: "a", type: "nominal" },
y: { field: "b", type: "quantitative" },
},
})
// Render directly
const { componentName, props } = fromConfig(config)
// → componentName: "BarChart", props: { data, categoryAccessor: "a", valueAccessor: "b" }
// Or generate JSX code
configToJSX(config)
// → <BarChart data={[...]} categoryAccessor="a" valueAccessor="b" />Supports bar, line, area, point, rect, arc, tick marks with encoding translation for color, size, aggregation, and binning.
Import only what you need:
import { LineChart } from "semiotic/xy" // 123 KB
import { BarChart } from "semiotic/ordinal" // 118 KB
import { ForceDirectedGraph } from "semiotic/network" // 127 KB
import { LineChart } from "semiotic/ai" // HOC-only surface for AI generationGranular entry points export only v3 Stream Frames and HOC charts — no legacy utilities or backwards-compatibility shims.
Built with strict: true. Full type definitions ship with the package.
Generics for type-safe accessors:
interface Sale { month: number; revenue: number }
<LineChart<Sale>
data={sales}
xAccessor="month" // TS validates this is keyof Sale
yAccessor="revenue"
/>Static SVG generation for Node.js (email, OG images, PDF):
import { renderToStaticSVG } from "semiotic/server"
const svg = renderToStaticSVG("xy", {
lines: [{ coordinates: data }],
xAccessor: "date",
yAccessor: "value",
size: [600, 400],
})Works with Next.js App Router, Remix, and Astro via "use client" directives.
- Getting Started
- Charts — all 27 chart types with live examples
- Frames — full Frame API reference
- Features — axes, annotations, tooltips, styling, Vega-Lite translator
- Cookbook — advanced patterns and recipes
- Playground — interactive prop exploration
- Migration Guide — upgrading from v1.x or v2.x
- Changelog — full release history
See CONTRIBUTING.md. Our community follows the nteract Code of Conduct.
Development of this library owes a lot to Susie Lu, Jason Reid, James Womack, Matt Herman, Shelby Sturgis, and Tristan Reid.
The Sankey layout engine is based on sankey-plus
by Tom Shanley, which improved on his earlier
d3-sankey-circular with better cycle detection, hierarchical arc stacking,
and dynamic extent adjustment.
Semiotic icon based on an icon by Andre Schauer.
