Choosing a React Chart Library: Recharts vs. ECharts vs. Nivo vs. Lightweight Charts

Before picking a chart library, figure out which chart type matters most to you—the design priorities of these libraries differ wildly, and there is no single all-around champion.

This article looks at the three most common chart types: candlestick charts (the heart of financial apps), bar charts (the staple of every dashboard), and treemaps (hierarchical data visualization). For each one, I assess what the major libraries can actually do and give concrete recommendations. The data comes from public npm/GitHub stats as of April 2026.

The Landscape: Seven Mainstream Options

LibraryPositioningGitHub StarsWeekly Downloads
RechartsDeclarative charts for React27k3.6M
Apache ECharts (echarts-for-react)Most feature-complete, config-driven64k800k
NivoPolished animations + accessibility13.6k450k
TradingView Lightweight ChartsProfessional finance/candlesticks15k560k
Visx (Airbnb)Thin wrappers over D3 primitives20k300k
VictoryReact Native compatibility (main selling point)11.2k272k
Chart.js (react-chartjs-2)General-purpose Canvas chartsChart.js 65kreact-chartjs-2 ~2.5M / chart.js ~4.1M

Victory’s core selling point is React Native compatibility—the same components work on both the web and in React Native apps. For web-only projects, Recharts or Nivo is usually a better choice; the rest of this article skips Victory and focuses on the libraries more commonly used on the web.

Overall Comparison

DimensionRechartsEChartsNivoLightweight ChartsVisxChart.js
CandlestickWorkaroundNative supportNot supportedPurpose-builtCompose yourselfNeeds a plugin
Bar chartsFullMost completeFull + Canvas variantBasic onlyFully customFull
TreemapBasic supportSupported + drill-downThree render modesNot supported@visx/hierarchyNot supported
RenderingSVGCanvas / WebGLSVG + CanvasCanvasSVG + CanvasCanvas
Large datasetsNoticeable jank at 1k+Millions of pointsCanvas variant decentOptimized for real-time financeDepends on implementationMillions
Bundle (gzip)~50 KB~80-130 KB (à la carte)~82 KB~12 KB (gzip)~30-50 KB~66 KB
Learning curveLowMedium-highMediumMedium (finance-specific)High (requires D3 knowledge)Low
TypeScriptGood since v3GoodGoodExcellent (written in TS)Excellent (written in TS)Good
SSR supportLimitedLimitedNativeNot supportedNeeds configurationNot supported
MaintenanceActiveVery activeActiveVery activeSlow release cadenceActive

Candlestick Charts: Which Library Actually Works?

TradingView Lightweight Charts — The Only Professional Choice

If candlestick charts are central to your project, Lightweight Charts is the only option that requires no compromises.

It’s purpose-built for financial time-series data: candlesticks, lines, areas, and volume histograms are all first-class citizens. The data format is minimal:

import { createChart } from "lightweight-charts";

const chart = createChart(document.getElementById("chart"), {
  width: 800,
  height: 400,
});

const candleSeries = chart.addCandlestickSeries({
  upColor: "#26a69a",
  downColor: "#ef5350",
  borderVisible: false,
  wickUpColor: "#26a69a",
  wickDownColor: "#ef5350",
});

candleSeries.setData([
  { time: "2024-01-01", open: 100, high: 110, low: 95, close: 105 },
  { time: "2024-01-02", open: 105, high: 115, low: 100, close: 98 },
  { time: "2024-01-03", open: 98, high: 108, low: 92, close: 103 },
]);

Using it in React (the useRef + useEffect pattern):

import { useEffect, useRef } from "react";
import { createChart, IChartApi } from "lightweight-charts";

interface CandleData {
  time: string;
  open: number;
  high: number;
  low: number;
  close: number;
}

export function CandlestickChart({ data }: { data: CandleData[] }) {
  const containerRef = useRef<HTMLDivElement>(null);
  const chartRef = useRef<IChartApi | null>(null);

  useEffect(() => {
    if (!containerRef.current) return;

    const chart = createChart(containerRef.current, {
      layout: { background: { color: "#1a1a2e" }, textColor: "#d1d4dc" },
      grid: { vertLines: { color: "#2a2a3c" }, horzLines: { color: "#2a2a3c" } },
      width: containerRef.current.clientWidth,
      height: 400,
    });

    const series = chart.addCandlestickSeries();
    series.setData(data);
    chart.timeScale().fitContent();

    chartRef.current = chart;

    const handleResize = () => {
      if (containerRef.current) {
        chart.applyOptions({ width: containerRef.current.clientWidth });
      }
    };
    window.addEventListener("resize", handleResize);

    return () => {
      window.removeEventListener("resize", handleResize);
      chart.remove();
    };
  }, [data]);

  return <div ref={containerRef} />;
}

v5 (the latest as of April 2026) has a 35 KB base bundle (uncompressed), about 12 KB gzipped—the smallest of any chart library. It renders to Canvas and stays smooth even with real-time tick-level updates (hundreds of data refreshes per second).

The limitation: it only does financial charts. If your dashboard also needs pie charts, heatmaps, or scatter plots, you’ll have to pull in a second library.

Apache ECharts — Candlesticks Plus Volume

Price-volume charts (candlesticks stacked with volume bars) are extremely common in financial products, and ECharts lets you overlay a candlestick series and a bar series in the same chart instance directly:

import * as echarts from "echarts/core";
import { CandlestickChart, BarChart } from "echarts/charts";
import { GridComponent, TooltipComponent, DataZoomComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";

echarts.use([CandlestickChart, BarChart, GridComponent, TooltipComponent, DataZoomComponent, CanvasRenderer]);

const option = {
  xAxis: { data: dates },
  yAxis: [
    { scale: true },           // K 线坐标轴
    { scale: true, show: false }, // 成交量坐标轴(隐藏)
  ],
  series: [
    {
      type: "candlestick",
      data: klineData, // [open, close, low, high]
      yAxisIndex: 0,
    },
    {
      type: "bar",
      data: volumeData,
      yAxisIndex: 1,
      itemStyle: {
        color: (params: any) => params.data[1] >= params.data[0] ? "#26a69a" : "#ef5350",
      },
    },
  ],
  dataZoom: [{ type: "inside" }, { type: "slider" }],
};

The built-in dataZoom component (mouse-wheel zooming, slider-based range selection) is genuinely useful in financial scenarios and requires no extra development.

Recharts — The Candlestick Trap

The official Recharts examples show how to fake a candlestick chart with <Bar> + <ErrorBar>, but it’s only an approximation:

// 这种实现的问题:
// 1. 阳线/阴线颜色需要用 Cell 逐个设置,数据量大时性能差
// 2. 影线(上下 wick)和实体颜色联动逻辑需要手写
// 3. 没有金融图表需要的十字光标、价格标签等组件
// 4. 代码量是 Lightweight Charts 的 3-5 倍

If your project only occasionally shows a candlestick chart, this is acceptable. But if candlesticks are a core feature, the maintenance cost will be steep.


Bar Charts: Why Recharts Is the Default Choice

Recharts — The React-Friendly Pick

Recharts is the most-downloaded React-specific chart library (3.6M weekly downloads), and the main reason is that its composition model matches React’s perfectly:

import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Cell, LabelList } from "recharts";

const data = [
  { month: "Jan", revenue: 4200, cost: 2400 },
  { month: "Feb", revenue: 3800, cost: 1900 },
  { month: "Mar", revenue: 5100, cost: 2800 },
  { month: "Apr", revenue: 4700, cost: 2200 },
];

// 堆叠柱状图
export function StackedBar() {
  return (
    <BarChart width={600} height={300} data={data}>
      <CartesianGrid strokeDasharray="3 3" />
      <XAxis dataKey="month" />
      <YAxis />
      <Tooltip />
      <Legend />
      <Bar dataKey="revenue" stackId="a" fill="#6366f1" />
      <Bar dataKey="cost" stackId="a" fill="#f43f5e" />
    </BarChart>
  );
}

// 单个柱子自定义颜色(Cell)
export function CustomColorBar() {
  const colors = ["#6366f1", "#8b5cf6", "#a78bfa", "#c4b5fd"];
  return (
    <BarChart width={600} height={300} data={data}>
      <Bar dataKey="revenue">
        {data.map((_, index) => (
          <Cell key={index} fill={colors[index % colors.length]} />
        ))}
        <LabelList dataKey="revenue" position="top" />
      </Bar>
    </BarChart>
  );
}

Stacking with stackId, per-bar colors with Cell, value labels with LabelList—everything is declarative, with no config-object paths to memorize.

The limitation: SVG rendering means noticeable jank once you exceed roughly 1,000 data points. When a bar chart has lots of bars (e.g., per-minute time-series over 24 hours = 1,440 points), consider switching to ECharts or Chart.js.

Apache ECharts — Big Data and Advanced Interactions

When your dataset exceeds 10k points, or you need brush selection, animated re-sorting, and other advanced interactions, ECharts is the better fit:

// 动态排序柱状图(ECharts 内置功能,Recharts 需要自实现)
const option = {
  xAxis: { max: "dataMax" },
  yAxis: { type: "category", data: categories, animationEasing: "linear" },
  series: [{
    type: "bar",
    data: values,
    realtimeSort: true,  // 实时排序动画
    label: { show: true, position: "right" },
  }],
  animationDuration: 0,
  animationDurationUpdate: 2000,
};

ECharts v6 (released in July 2025) added a matrix coordinate system, further extending what bar charts can express.

Nivo — Visual Polish and Accessibility

If your product has accessibility requirements (WCAG 2.1 AA), Nivo offers some of the most complete out-of-the-box accessibility support among mainstream React chart libraries. Its animations are powered by @react-spring, and the transitions are the most polished:

import { ResponsiveBar } from "@nivo/bar";

<ResponsiveBar
  data={data}
  keys={["revenue", "cost"]}
  indexBy="month"
  margin={{ top: 50, right: 130, bottom: 50, left: 60 }}
  padding={0.3}
  colors={{ scheme: "nivo" }}
  animate={true}          // 基于 @react-spring 的动画
  motionConfig="gentle"   // 动画缓动配置
  role="application"      // 无障碍 ARIA
  ariaLabel="Revenue and cost by month"
/>

For larger datasets, switch to <BarCanvas> to render with Canvas instead of SVG—the API stays the same.


Treemaps: Nivo Leads

Nivo @nivo/treemap — Three Render Modes

Nivo’s treemap supports SVG, Canvas, and HTML rendering, and nodeComponent lets you fully customize each node:

import { ResponsiveTreeMap } from "@nivo/treemap";

const data = {
  name: "root",
  children: [
    {
      name: "Frontend",
      children: [
        { name: "React", size: 85000 },
        { name: "Vue", size: 42000 },
        { name: "Angular", size: 38000 },
      ],
    },
    {
      name: "Backend",
      children: [
        { name: "Node.js", size: 62000 },
        { name: "Python", size: 71000 },
      ],
    },
  ],
};

<ResponsiveTreeMap
  data={data}
  identity="name"
  value="size"
  valueFormat=".02s"
  margin={{ top: 10, right: 10, bottom: 10, left: 10 }}
  labelSkipSize={12}
  labelTextColor={{ from: "color", modifiers: [["darker", 1.2]] }}
  parentLabelPosition="left"
  parentLabelTextColor={{ from: "color", modifiers: [["darker", 2]] }}
  colors={{ scheme: "tableau10" }}
/>

If you need full control over how each tile renders, Nivo’s nodeComponent prop accepts a custom React component governing each node’s appearance and interactions—e.g., embedding a sparkline, a progress bar, or arbitrary SVG inside a tile. See the nodeComponent docs at nivo.rocks/treemap for the exact API.

Apache ECharts — Hierarchical Drill-Down

If your treemap needs click-to-zoom into parent nodes (drill-down) with breadcrumb navigation, ECharts has it built in:

const option = {
  series: [{
    type: "treemap",
    data: hierarchicalData,
    leafDepth: 1,     // 默认显示到第几层
    drillDownIcon: "",
    breadcrumb: { show: true },  // 显示层级导航
    levels: [
      { itemStyle: { borderWidth: 3, gapWidth: 3 } },
      { itemStyle: { borderWidth: 2, gapWidth: 2 } },
      { colorSaturation: [0.35, 0.5] },
    ],
  }],
};

Performance: When Is SVG Not Enough?

The rendering technology sets the performance ceiling:

RenderingSuitable Data VolumeCharacteristics
SVG< 1,000 pointsMany DOM nodes, but styleable with CSS and SSR-friendly
Canvas 2D< 1 million pointsStrong performance, but no per-element CSS control
WebGL> 1 million pointsExtreme performance, supported by ECharts GL

Recharts uses SVG, and frame rates drop noticeably past 1,000 data points. ECharts defaults to Canvas and renders smoothly even with 100k points.

Reference benchmark (MacBook Pro M3, Chrome 122, bar chart, default configuration):

Data pointsRecharts (SVG)ECharts (Canvas)Chart.js (Canvas)
500SmoothSmoothSmooth
2,000Slight jankSmoothSmooth
10,000Noticeable jankSmoothSmooth
100,000UnusableSmoothMostly smooth

These are reference figures only—actual performance depends on data shape, animation settings, and chart type. Benchmark on your target devices.


Bundle Size Comparison

Importing à la carte is key. The full ECharts package is ~340 KB (gzip); importing only the components you use cuts that down significantly—a BarChart + Tooltip + Grid + DataZoom combination comes to roughly 80-130 KB (gzip; the exact number depends on the component mix, so measure with bundlejs.com):

// 按需引入 ECharts(推荐写法)
import * as echarts from "echarts/core";
import { BarChart, CandlestickChart } from "echarts/charts";
import {
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DataZoomComponent,
} from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";

echarts.use([
  BarChart,
  CandlestickChart,
  TitleComponent,
  TooltipComponent,
  GridComponent,
  DataZoomComponent,
  CanvasRenderer,
]);

Recharts v3 sets sideEffects: false and ships ES Module output, so Vite/Webpack 5 can tree-shake it—but its core dependencies (d3, victory-vendor, etc.) are heavy, so the real-world gain is limited and the full package still gzips to about 50 KB.


Decision Tree

Need candlestick charts?
  ├── Candlesticks are the core feature → Lightweight Charts (purpose-built, smallest bundle)
  └── Candlesticks + other chart types → Apache ECharts (one library for everything)

No candlesticks needed:
  ├── Dataset > 10k points → Apache ECharts (Canvas performance)
  ├── Need treemaps + polished animations → Nivo
  ├── Highly custom visuals (D3 experience) → Visx
  └── General dashboard, fast development → Recharts

Quick Reference by Scenario

ScenarioRecommendationWhy
Pure financial candlesticksLightweight ChartsPurpose-built, ~12 KB gzip, best Canvas performance
Candlesticks + volume barsApache EChartsNative series overlay, built-in dataZoom
General business dashboardRechartsLowest learning cost, biggest community
Datasets of 10k+ pointsApache EChartsCanvas/WebGL rendering
Treemap with drill-downApache EChartsBuilt-in drill-down and breadcrumb navigation
Treemap with custom nodesNivoFull customization via nodeComponent
Accessibility (WCAG 2.1 AA)NivoAmong the most complete out-of-the-box a11y support
Maximum customizationVisxD3 primitives, full control
Quick prototypingChart.jsMost documentation, richest community Q&A

Conclusion

No single chart library fits every scenario. In real projects, the two most common combinations are:

Option A (finance/trading products): Lightweight Charts (candlesticks) + Recharts or ECharts (everything else)

Option B (general SaaS dashboards): Apache ECharts (the workhorse—fast and feature-complete) or Recharts + Nivo (React-friendly + polished visuals, for moderate data volumes)

If you can only pick one and performance matters, Apache ECharts is the single choice with the broadest coverage: candlesticks, bar charts, and treemaps are all natively supported, Canvas rendering handles big data, and à la carte imports keep the bundle in check. If your team is fluent in React and the data volumes are modest, Recharts offers a developer experience closest to React idioms with the lowest ramp-up cost.