OpenChart Is Now Open Source

The charting library behind OpenData's visualizations is now free and open source. Specs-driven, headless, framework-agnostic, and built for publication-quality output.

Riley Hilliard
Riley Hilliard
Creator of OpenData·Mar 1, 2026·6 min
Copied to clipboard

There’s a gap in charting libraries that nobody talks about directly, but everyone has felt. You want your charts to look like they belong in The Economist or a Chartr newsletter. Clean typography, proper source attribution, a headline that carries the insight. But the library you’re using gives you a default bar chart that screams “made with a library,” and the path from there to editorial polish is 200 lines of config overrides, CSS hacks, and fighting the framework’s opinions about where labels should go.

Most teams never cross that gap. They ship the default look, maybe adjust the colors, and move on. Not because they don’t care about quality, but because the effort required to get from “functional chart” to “chart worth sharing” is wildly disproportionate to what they’re trying to communicate.

Today we’re open-sourcing OpenChart, the library that powers all the data visualizations on OpenData. It’s free, MIT-licensed, and available now at github.com/tryopendata/openchart.

OpenChart Preview
Oct 20222023Apr 2023Jul 2023Oct 20232024Apr 2024Jul 2024Oct 20242025120140160180200220240Share PriceSVB collapse2-yr avg: $181Record close: $243March 2023 lowApple Shares Hit Record After AI-Fuelled RallyAAPL monthly closing price, Jan 2023 to Dec 2024Source: Nasdaq historical data

What OpenChart Actually Is

OpenChart is a spec-driven visualization library. You describe what the chart should communicate in a JSON spec, and the engine handles everything else: scales, axes, labels, colors, responsive layout, accessibility.

const spec = {
  type: "bar",
  data: [
    { category: "Food", value: 332 },
    { category: "Housing", value: 287 },
    { category: "Transport", value: 198 },
  ],
  encoding: {
    x: { field: "category", type: "nominal" },
    y: { field: "value", type: "quantitative" },
    color: { field: "category", type: "nominal" },
  },
  chrome: {
    title: "CPI by Category",
    subtitle: "Consumer Price Index, December 2025",
    source: "Bureau of Labor Statistics",
  },
};

The spec IS the API. Not a component tree. Not imperative drawing calls. A declarative description that can be stored in a database, generated by an LLM, version-controlled, or validated before anything touches the screen. You can diff two specs to see exactly what changed between chart versions. You can generate them programmatically and inspect them before rendering. The spec serializes as plain JSON, so it works anywhere JSON works.

The encoding model follows Vega-Lite conventions. Each channel (x, y, color, size) maps a field from your data to a visual property, with a type that tells the engine how to interpret the values: quantitative for continuous numbers, temporal for dates, nominal for unordered categories, ordinal for ordered ones. The engine picks the right scale type automatically. A temporal x-axis gets a time scale. A nominal color encoding gets a categorical palette from the theme.

Headless Architecture

The engine is split into packages with a strict dependency chain:

core → engine → vanilla → react/vue/svelte

The key insight: the math layer (@openchart/engine) has zero DOM dependencies. It takes a spec plus dimensions and produces a layout object with every position computed to the pixel. Scales, tick marks, label positions, mark coordinates, annotation anchors, accessibility metadata. All of it resolved to concrete values. The renderers (@openchart/vanilla, @openchart/react, etc.) are thin layers that walk the layout object and draw.

This split has real consequences for how you work. You can test chart logic with plain Vitest, no jsdom, no browser. “Does this dataset produce the right Y-axis domain?” is a unit test that runs in milliseconds. You can render on the server without Puppeteer. The same compilation step produces identical output in Node.js and the browser. And if you need to support a framework that doesn’t have a wrapper yet, you’re writing a thin rendering adapter, not reimplementing chart math.

The @openchart/vanilla package handles SVG and HTML rendering, resize observation, and chart exports. The framework-specific packages (@openchart/react, @openchart/vue, @openchart/svelte) are lifecycle bridges. The React <Chart /> component is roughly 30 lines: it creates a ref, calls createChart() on mount, and chart.update(spec) when props change. That’s it.

Editorial-First Design

Most chart libraries treat titles and source attribution as afterthoughts. You pass a string, the library sticks it somewhere with default styling, and you’re left tweaking font sizes in CSS. Annotations are worse: most libraries either don’t support them or offer escape hatches into the raw SVG.

OpenChart treats these as first-class structural elements because that’s what separates a chart from a visualization. The core principles: the headline IS the insight, one chart communicates one thing, minimal chrome, strategic annotation. Those principles are baked into OpenChart’s chrome system. Titles, subtitles, source attribution, and annotations are all first-class fields in the spec, not afterthoughts you bolt on with CSS.

Annotations come in three types: reference lines (baselines, thresholds), range highlights (recession bands, target zones), and text callouts (labels pointing to key data points with connector lines). These are what turn a chart from “here’s some data” into “here’s the story.” A reference line at the national average. A shaded band marking a recession. A callout on the inflection point where the trend reversed. All declarative, all in the spec, all positioned by the engine’s math layer.

What You Can Build

Chart types: line, area, bar, column, scatter, dot, pie, and donut. Each with multi-series support via the color encoding channel. Scatter charts support bubble sizing and built-in trendlines. Donut is the same engine as pie with a non-zero inner radius.

Rich data tables go well beyond basic HTML: sorting, search, pagination, heatmap cells (color-coded by value), inline bars (mini bar charts in each cell), and sparklines (embedded line, bar, or column mini-charts showing trends). The table compiler handles all the layout math headlessly, same as charts.

Force-directed network graphs with canvas rendering handle larger datasets where SVG would choke. Nodes, edges, labels, collision detection, all computed by the engine and rendered to a canvas element.

All chart types support dark mode, responsive breakpoints, deep-mergeable theme configuration, and export to SVG, PNG, or CSV.

Accessibility

Accessibility is computed during compilation, not bolted on after rendering. This is a meaningful distinction because it means accessibility is testable without a browser.

The engine auto-generates alt text from the spec and data. “Bar chart showing CPI by Category across 3 categories (3 data points).” Every mark in the layout carries an ARIA label. “Food: 332.” The renderer maps these to aria-label attributes on the corresponding SVG elements.

Color-blindness simulation uses Brettel, Vienot, and Mollon matrices for protanopia, deuteranopia, and tritanopia. checkPaletteDistinguishability() takes a palette and a deficiency type, simulates how each color appears, and verifies that all pairs maintain sufficient perceptual distance. WCAG contrast checking is built into the color system with contrastRatio(), meetsAA(), and findAccessibleColor() (which binary-searches for an adjusted variant that preserves hue while meeting the 4.5:1 threshold).

You can verify all of this in a unit test running in Node. No browser, no screen reader emulation. Just assertions against the compiled layout.

Getting Started

Installation is one line:

npm install @openchart/react
# or
npm install @openchart/vue
npm install @openchart/svelte
npm install @openchart/vanilla

The live demo has interactive examples for every chart type, annotation editing, dark mode, responsive layouts, and table features.

Source is on GitHub: github.com/tryopendata/openchart

If you’ve ever spent more time fighting your chart library than analyzing your data, give OpenChart a look. And if you build something cool with it, we’d love to see it.

Riley Hilliard
Riley Hilliard

Creator of OpenData

At 13, I secretly drilled holes in my parents' wood floor to route a 56k modem line to my bedroom for late-night Age of Empires marathons. That same scrappy curiosity carried through 3 acquisitions, 9 years as a LinkedIn Staff Engineer building infrastructure for 1B+ users, and now fuels my side projects, like OpenData.

Copied to clipboard

More from OpenData

Why Your Charts Don't Get Shared (And Chartr's Do)

Chartr grew to 500K+ subscribers by making data visualization shareable. What they figured out about headline-first framing, minimal chrome, and social optimization applies to anyone making charts.

Riley HilliardRiley Hilliard·Mar 26, 2026

Store Flat, Transform on Read

Why we store all data in long format and apply transforms at query time instead of pre-computing views. A technical deep dive into DuckDB, Parquet, and the architecture behind OpenData's query engine.

Riley HilliardRiley Hilliard·Mar 19, 2026

70% of AI Training Datasets Have the Wrong License

A large-scale audit found that over 70% of popular AI datasets have missing or wrong license metadata. With the EU AI Act now enforcing training data transparency, this isn't just sloppy. It's a liability.

Riley HilliardRiley Hilliard·Mar 12, 2026

Public Data Has a Discovery Problem

Government data is technically public but practically inaccessible. Here's what that actually costs researchers, journalists, and anyone trying to answer a question with data.

Riley HilliardRiley Hilliard·Mar 5, 2026

The Hidden Mess Inside 'Clean' Government Data

Government data has a reputation for being clean and reliable. Anyone who's tried to ingest it programmatically knows that's not the full story. Here are the real encoding quirks, format traps, and silent failures hiding in data from FRED, BLS, Census, the World Bank, and the EPA.

Riley HilliardRiley Hilliard·Feb 19, 2026

The State of Open Data Infrastructure in 2026

A survey of the open data landscape: what data.gov, Socrata, FRED, Kaggle, Hugging Face, and Datasette do well, what's still broken, and where the connective tissue between data sources is finally being built.

Riley HilliardRiley Hilliard·Feb 12, 2026

Building a Headless Visualization Engine

How we separated chart computation from rendering by building a spec-driven visualization engine. The architecture behind @opendata/viz: four packages, a compilation pipeline, and zero DOM dependencies in the math layer.

Riley HilliardRiley Hilliard·Feb 5, 2026

Bootstrapping a Data Platform on Two Mac Minis

OpenData runs in production on two Mac Minis at $0/month infrastructure cost. Here's the architecture, the tradeoffs, and the specific triggers that would move us to cloud.

Riley HilliardRiley Hilliard·Jan 29, 2026

What Happens When All the World's Open Data Lives in One Place

Open data has a discovery problem, not an access problem. When you centralize datasets from hundreds of portals, entirely new capabilities emerge: knowledge graphs that reveal hidden connections, bridge datasets that make cross-agency joins possible, and a compounding network where every new dataset makes every existing one more useful.

Riley HilliardRiley Hilliard·Jan 22, 2026

Curious about open data? Start exploring.

OpenData makes public datasets discoverable, consistently formatted, and queryable without the usual headaches.

Try it out
  • Browse thousands of public datasets
  • Query any dataset with a simple API
  • Download as CSV, JSON, or Parquet