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.
| If you want | Pick |
|---|---|
| Quick charts from components | Recharts |
| Small bundle, many data points | Chart.js |
| A design of your own | visx |
| Huge datasets, maps, heavy interaction | ECharts |
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.
| Library | Version | Minified | Gzipped |
|---|---|---|---|
| visx | 4.0.0 | 68.7 KB | 24.6 KB |
| Chart.js + react-chartjs-2 | 4.5.1 / 5.3.1 | 154.1 KB | 54.2 KB |
| Nivo (@nivo/bar) | 0.99.0 | 267.6 KB | 93.8 KB |
| Recharts | 3.10.1 | 384.8 KB | 116.0 KB |
| Victory | 37.3.6 | 424.3 KB | 135.1 KB |
| ECharts, modular import (echarts/core) | 6.1.0 | 558.2 KB | 193.3 KB |
| ECharts, full import | 6.1.0 | 1,180.4 KB | 399.9 KB |
| Library | Draws with | Style of API | Licence | Good fit |
|---|---|---|---|---|
| Recharts | SVG | React components (BarChart, XAxis, Bar) | MIT | Dashboards in React with little setup |
| Chart.js | Canvas | Config object, React wrapper | MIT | Many points, small bundle, familiar look |
| Nivo | SVG, canvas or HTML per chart | One component per chart, many props | MIT | Polished defaults, many chart types |
| Victory | SVG | Composable components | MIT | Charts shared with React Native |
| visx | SVG | Low-level parts (scales, shapes, axes) | MIT | Custom designs, smallest bundle |
| ECharts | Canvas or SVG | One big option object | Apache-2.0 | Large datasets, maps, complex interactive charts |
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 },
];
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 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>
);
}
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>
);
}
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>
);
}
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>
);
}
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] } }],
}}
/>
);
}
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] } }],
}}
/>
);
}
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.
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.
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.
Yes. All six are open source: Recharts, Chart.js, Nivo, Victory and visx under MIT, and ECharts under Apache 2.0.
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.
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.