React Chart Libraries Compared: One Chart, Six Libraries

For most React dashboards, Recharts is the quickest start and Chart.js gives a smaller bundle drawn on canvas. Pick visx for fully custom designs at the smallest size, Nivo for polished defaults, Victory if you also target React Native, and ECharts for very large or complex charts. We built the same chart in each and measured it.

Short version
If you wantPick
Quick charts from componentsRecharts
Small bundle, many data pointsChart.js
A design of your ownvisx
Huge datasets, maps, heavy interactionECharts

Updated

Every library below draws the same six-month bar chart from the same data. We bundled each one on its own and measured the result, so the size table compares like with like. The Recharts example also runs live on this page.

Need somewhere to put the charts? Start from our dashboard layouts.

Bundle size, measured

Measured: one bar chart, minified and gzipped (React not counted)
LibraryVersionMinifiedGzipped
visx4.0.068.7 KB24.6 KB
Chart.js + react-chartjs-24.5.1 / 5.3.1154.1 KB54.2 KB
Nivo (@nivo/bar)0.99.0267.6 KB93.8 KB
Recharts3.10.1384.8 KB116.0 KB
Victory37.3.6424.3 KB135.1 KB
ECharts, modular import (echarts/core)6.1.0558.2 KB193.3 KB
ECharts, full import6.1.01,180.4 KB399.9 KB

Side by side

How they differ
LibraryDraws withStyle of APILicenceGood fit
RechartsSVGReact components (BarChart, XAxis, Bar)MITDashboards in React with little setup
Chart.jsCanvasConfig object, React wrapperMITMany points, small bundle, familiar look
NivoSVG, canvas or HTML per chartOne component per chart, many propsMITPolished defaults, many chart types
VictorySVGComposable componentsMITCharts shared with React Native
visxSVGLow-level parts (scales, shapes, axes)MITCustom designs, smallest bundle
EChartsCanvas or SVGOne big option objectApache-2.0Large datasets, maps, complex interactive charts

The same chart in six libraries

The data every example uses

Six months of website visits. Each library below draws the same bar chart from it.

export const data = [
  { month: "Jan", visits: 1200 }, { month: "Feb", visits: 1650 }, { month: "Mar", visits: 1420 },
  { month: "Apr", visits: 2100 }, { month: "May", visits: 2480 }, { month: "Jun", visits: 2310 },
];

Recharts

Composed from components that read like the chart: an x axis, a y axis, a tooltip, bars. ResponsiveContainer makes it fill its parent's width. The live chart beside it is this code.

import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
import { data } from "./data.js";

export function VisitsChart() {
  return (
    <ResponsiveContainer width="100%" height={260}>
      <BarChart data={data}>
        <XAxis dataKey="month" />
        <YAxis />
        <Tooltip />
        <Bar dataKey="visits" fill="#4f98fb" radius={[6, 6, 0, 0]} />
      </BarChart>
    </ResponsiveContainer>
  );
}

Chart.js with react-chartjs-2

Chart.js is tree-shakeable, so you register only the pieces the chart uses. It draws to a canvas, which stays fast with thousands of points but needs its own accessible text.

import { BarElement, CategoryScale, Chart as ChartJS, LinearScale, Tooltip } from "chart.js";
import { Bar } from "react-chartjs-2";
import { data } from "./data.js";

ChartJS.register(CategoryScale, LinearScale, BarElement, Tooltip);

export function VisitsChart() {
  return (
    <div style={{ height: 260 }}>
      <Bar
        data={{
          labels: data.map((d) => d.month),
          datasets: [{ label: "Visits", data: data.map((d) => d.visits), backgroundColor: "#4f98fb", borderRadius: 6 }],
        }}
        options={{ maintainAspectRatio: false, plugins: { legend: { display: false } } }}
      />
    </div>
  );
}

Nivo

One component with many props and good-looking defaults. ResponsiveBar needs a parent with a set height.

import { ResponsiveBar } from "@nivo/bar";
import { data } from "./data.js";

export function VisitsChart() {
  return (
    <div style={{ height: 260 }}>
      <ResponsiveBar
        data={data}
        keys={["visits"]}
        indexBy="month"
        colors={["#4f98fb"]}
        borderRadius={6}
        margin={{ top: 10, right: 10, bottom: 30, left: 50 }}
        enableLabel={false}
      />
    </div>
  );
}

Victory

Composable SVG components. The same API exists for React Native (victory-native), which helps when the web and app share chart code.

import { VictoryAxis, VictoryBar, VictoryChart, VictoryTooltip } from "victory";
import { data } from "./data.js";

export function VisitsChart() {
  return (
    <VictoryChart domainPadding={20} height={260}>
      <VictoryAxis />
      <VictoryAxis dependentAxis />
      <VictoryBar
        data={data}
        x="month"
        y="visits"
        labels={({ datum }) => datum.visits}
        labelComponent={<VictoryTooltip />}
        style={{ data: { fill: "#4f98fb" } }}
        cornerRadius={{ top: 6 }}
      />
    </VictoryChart>
  );
}

visx

Not a chart library so much as building blocks: you make the scales and draw the bars yourself. More code, total control, smallest bundle in our test.

import { AxisBottom, AxisLeft } from "@visx/axis";
import { Group } from "@visx/group";
import { scaleBand, scaleLinear } from "@visx/scale";
import { Bar } from "@visx/shape";
import { data } from "./data.js";

const width = 480, height = 260, m = { top: 10, right: 10, bottom: 30, left: 50 };
const x = scaleBand({ domain: data.map((d) => d.month), range: [0, width - m.left - m.right], padding: 0.3 });
const y = scaleLinear({ domain: [0, Math.max(...data.map((d) => d.visits))], range: [height - m.top - m.bottom, 0], nice: true });

export function VisitsChart() {
  return (
    <svg width={width} height={height} role="img" aria-label="Monthly visits">
      <Group left={m.left} top={m.top}>
        {data.map((d) => (
          <Bar key={d.month} x={x(d.month)} y={y(d.visits)} width={x.bandwidth()}
            height={y(0) - y(d.visits)} fill="#4f98fb" rx={6} />
        ))}
        <AxisLeft scale={y} />
        <AxisBottom scale={x} top={y(0)} />
      </Group>
    </svg>
  );
}

ECharts (full import)

Everything is one option object. Importing echarts-for-react pulls in the whole of ECharts, which is the largest bundle here.

import ReactECharts from "echarts-for-react";
import { data } from "./data.js";

export function VisitsChart() {
  return (
    <ReactECharts
      style={{ height: 260 }}
      option={{
        tooltip: {},
        xAxis: { type: "category", data: data.map((d) => d.month) },
        yAxis: { type: "value" },
        series: [{ type: "bar", data: data.map((d) => d.visits), itemStyle: { color: "#4f98fb", borderRadius: [6, 6, 0, 0] } }],
      }}
    />
  );
}

ECharts (modular import)

Import from echarts/core and register only the chart, components and renderer you use. In our test that cut the bundle by about half.

import ReactEChartsCore from "echarts-for-react/lib/core";
import { BarChart } from "echarts/charts";
import { GridComponent, TooltipComponent } from "echarts/components";
import * as echarts from "echarts/core";
import { SVGRenderer } from "echarts/renderers";
import { data } from "./data.js";

echarts.use([BarChart, GridComponent, TooltipComponent, SVGRenderer]);

export function VisitsChart() {
  return (
    <ReactEChartsCore
      echarts={echarts}
      style={{ height: 260 }}
      option={{
        tooltip: {},
        xAxis: { type: "category", data: data.map((d) => d.month) },
        yAxis: { type: "value" },
        series: [{ type: "bar", data: data.map((d) => d.visits), itemStyle: { color: "#4f98fb", borderRadius: [6, 6, 0, 0] } }],
      }}
    />
  );
}

How we measured

Each example on this page was bundled alone with esbuild in production mode, minified as an ES module with React and React DOM left out, then compressed with gzip at level 9. Real apps share code between charts, so a second chart of the same kind adds much less.

How it works

  1. Check the size table against your page weight budget.
  2. Read the comparison for rendering (SVG or canvas) and API style.
  3. Copy the example for the library you pick and swap in your data.
  4. Add a text summary or table of the numbers for screen reader users.

Tips

Frequently asked questions

What is the best chart library for React?

It depends on the job. Recharts suits typical dashboards, Chart.js suits lots of data with a small bundle, visx suits bespoke designs, and ECharts suits complex, data-heavy charts. The size table here shows the cost of each.

Is Recharts or Chart.js better?

Recharts is more natural in React because charts are components and render as SVG you can style. Chart.js draws on canvas, handles many points well and was less than half the size in our test.

Are these chart libraries free for commercial use?

Yes. All six are open source: Recharts, Chart.js, Nivo, Victory and visx under MIT, and ECharts under Apache 2.0.

Do React chart libraries work with Server Components?

They draw in the browser and use effects or refs, so the chart component needs "use client". The data can still be fetched in a Server Component and passed down as props.

Related: React memo, useMemo and useCallback, React data table examples, Dashboard layouts, JSON to CSV converter, All free tools.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap