Rreact.wiki
Open-Source Projects

visx as React’s Missing Primitives for Data Graphics

UI Components6/22/2026chartd3data-visualizationreactsvgvisualizationvisxvx
airbnb

airbnb/visx

21kTypeScriptMIT
chartd3data-visualizationreactsvgvisualizationvisxvx

visx is a collection of low-level, composable React components for building data visualizations—not prebuilt charts, but foundational building blocks like `<Bar />`, `<Line />`, and `<Axis />`, designed to work like native UI primitives.

If you’ve ever tried to build a custom chart in React—say, a timeline with interactive tooltips, a heatmap with dynamic filtering, or a dashboard where every chart shares the same theme—you’ve probably hit this wall: most charting libraries either give you too much (a rigid, opinionated API) or too little (raw D3 code that doesn’t play nice with React’s lifecycle).

Enter visx—not a charting library, but something more fundamental: React’s missing primitives for data graphics.

Think of div, button, or input. You don’t reach for a “prebuilt login form” when building an auth flow—you compose those primitives. visx brings that same philosophy to data visualization. It gives you <Bar />, <Line />, <Area />, <Axis />, <Group />, and <Scale />—not as finished charts, but as reusable, typed, React-native components you assemble like legos.

And yes—it has 20,901 stars, is written in TypeScript, MIT-licensed, and maintained by Airbnb. But its real power isn’t in scale—it’s in intention: visx exists so you never have to choose between D3’s precision and React’s declarative simplicity again.

The Pain visx Solves

Before visx, there were two common (and frustrating) patterns:

  • “D3-in-React”: You write D3 code inside useEffect, manually managing selections, enter/update/exit cycles, and DOM mutations. It works—but it’s brittle, hard to test, and breaks React’s mental model.
  • “Black-box chart libraries”: You drop in <LineChart data={...} />, tweak props like colorScheme="blue" or tooltip={true}, and hope it fits your design system. When it doesn’t? You’re stuck fork-and-patch—or rewrite from scratch.

visx bridges that gap. It uses D3 under the hood for scales, layouts, and math—but exposes everything as plain React components. No selections. No .attr(). No d3.transition(). Just props, state, and composition.

Key Features — Built Like React Primitives

  • Modular by design: Install only what you need (npm install @visx/shape @visx/scale @visx/group). No monolith. Each package is tiny (e.g., @visx/shape is ~2 KB gzipped).
  • TypeScript-first: Every component ships with full types—no any, no guessing what x or y accept.
  • SVG-native: All rendering happens in <svg>. No canvas, no webgl—just accessible, styleable, responsive vector graphics.
  • Framework-agnostic mindset: Works with Preact, Remix, Next.js, or plain React. No hidden dependencies.
  • No built-in interactivity or animation: That’s by design. Want hover effects? Use onMouseEnter. Animations? Plug in framer-motion or react-spring. visx stays out of your way.

A Hands-On Example: Building a Bar Chart — Step by Step

Let’s build a simple bar chart—not using a <BarChart /> component, but by composing low-level primitives. You’ll see how each piece maps to familiar React concepts.

First, install just what we need:

Bash
npm install @visx/mock-data @visx/group @visx/shape @visx/scale

Now, here’s the full runnable component (paste into any React 18+ app):

TSX
import React from 'react';
import { letterFrequency } from '@visx/mock-data';
import { Group } from '@visx/group';
import { Bar } from '@visx/shape';
import { scaleBand, scaleLinear } from '@visx/scale';
 
// Our data: letters and their frequencies
const data = letterFrequency.slice(0, 5); // top 5 letters
 
// Layout constants — just like CSS margin/padding
const width = 400;
const height = 200;
const margin = { top: 20, right: 20, bottom: 40, left: 40 };
 
// Inner chart area (excluding margins)
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
 
// Accessor functions — think of these like `item.name` in a list
const getLetter = (d: typeof data[0]) => d.letter;
const getFrequency = (d: typeof data[0]) => d.frequency;
 
// Scales — like CSS transforms: map data values → pixel positions
const xScale = scaleBand({
  range: [0, xMax],
  domain: data.map(getLetter),
  padding: 0.2,
});
const yScale = scaleLinear({
  range: [0, yMax],
  domain: [0, Math.max(...data.map(getFrequency))],
});
 
export default function SimpleBarChart() {
  return (
    <svg width={width} height={height}>
      {/* Background group — like a <div> wrapper */}
      <Group top={margin.top} left={margin.left}>
        {data.map((d, i) => {
          const barWidth = xScale.bandwidth();
          const barHeight = yScale(getFrequency(d));
          const barX = xScale(getLetter(d)) ?? 0;
          const barY = yMax - barHeight;
 
          return (
            <Group key={i}>
              {/* This is our "primitive" — no magic, just props */}
              <Bar
                x={barX}
                y={barY}
                width={barWidth}
                height={barHeight}
                fill="#4f46e5" // Tailwind indigo-600
              />
              {/* Optional: label below bar */}
              <text
                x={barX + barWidth / 2}
                y={yMax + 20}
                textAnchor="middle"
                fontSize={12}
                fill="#374151"
              >
                {getLetter(d)}
              </text>
            </Group>
          );
        })}
      </Group>
    </svg>
  );
}

That’s it. No config objects. No ref callbacks. No useEffect dance. Just props, JSX, and predictable rendering.

Notice how:

  • <Group> works like <div> — it applies transform to position children.
  • <Bar> is just a styled <rect> — you control x, y, width, height, and fill directly.
  • Scales are pure functions — call xScale('a') anywhere, anytime.

Who Is This For?

  • React developers who want full control over visuals but don’t want to learn D3’s imperative API.
  • Design systems teams building internal charting libraries — visx is the foundation, not the finish.
  • Data product engineers shipping dashboards where consistency, accessibility, and theming matter more than “quick chart” speed.
  • Educators and learners — because each component teaches how visualization works (scales, coordinate systems, SVG positioning), not just how to use it.

It’s not for folks who want “one prop to rule them all.” But if you believe charts should be as composable, testable, and maintainable as your buttons and forms—then visx isn’t just another library. It’s the set of primitives React should have shipped with for data interfaces.

Get started at https://airbnb.io/visx — and remember: you’re not building charts. You’re building data-driven UIs, one primitive at a time.