Skip to main content

Best JavaScript Charting Libraries in 2026

·PkgPulse Team

TL;DR

Chart.js for quick charts; Recharts for React-native data viz; D3 for custom, complex visualizations. Chart.js (~10M weekly downloads) is the most popular charting library with a simple config API. Recharts (~3M) wraps D3 in idiomatic React components. D3.js (~9M) is the power tool — a data transformation library, not just charting — steep learning curve but unlimited flexibility. For React apps, Recharts or Visx hits the right balance.

Key Takeaways

  • Chart.js: ~10M weekly downloads — most popular, canvas-based, simple config
  • D3.js: ~9M downloads — power tool for custom viz, SVG manipulation
  • Recharts: ~3M downloads — React-first, built on D3 scales + SVG
  • Visx: ~200K downloads — low-level React primitives from Airbnb, pairs with D3
  • ECharts: ~2M downloads — Apache project, best for dashboards, mobile-friendly

Chart.js (Simple, Fast)

// Chart.js — canvas-based, config-driven
import { Chart, registerables } from 'chart.js';
import { useEffect, useRef } from 'react';

Chart.register(...registerables);

function LineChart({ data }: { data: number[] }) {
  const canvasRef = useRef<HTMLCanvasElement>(null);

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

    const chart = new Chart(canvasRef.current, {
      type: 'line',
      data: {
        labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
        datasets: [{
          label: 'Downloads',
          data,
          borderColor: '#3B82F6',
          backgroundColor: 'rgba(59, 130, 246, 0.1)',
          tension: 0.4,
          fill: true,
        }],
      },
      options: {
        responsive: true,
        plugins: {
          legend: { display: true },
          tooltip: { mode: 'index' },
        },
        scales: {
          y: { beginAtZero: true },
        },
      },
    });

    return () => chart.destroy(); // Cleanup on unmount
  }, [data]);

  return <canvas ref={canvasRef} />;
}
// react-chartjs-2 — official React wrapper
import { Line, Bar, Pie, Doughnut } from 'react-chartjs-2';
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js';

// Tree-shakeable imports (only register what you need)
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend);

function DownloadTrends({ chartData }) {
  return (
    <Line
      data={chartData}
      options={{
        responsive: true,
        plugins: { legend: { position: 'top' } },
      }}
    />
  );
}

Best for: Dashboards with standard chart types, non-React projects, simplicity over customization.


Recharts (React-Native)

// Recharts — declarative React components
import {
  LineChart, Line, BarChart, Bar,
  XAxis, YAxis, CartesianGrid, Tooltip, Legend,
  ResponsiveContainer, Area, AreaChart,
} from 'recharts';

const data = [
  { month: 'Jan', downloads: 4000, installs: 2400 },
  { month: 'Feb', downloads: 3000, installs: 1398 },
  { month: 'Mar', downloads: 6000, installs: 5800 },
  { month: 'Apr', downloads: 8000, installs: 7000 },
];

function DownloadChart() {
  return (
    <ResponsiveContainer width="100%" height={400}>
      <AreaChart data={data} margin={{ top: 10, right: 30, left: 0, bottom: 0 }}>
        <defs>
          <linearGradient id="colorDownloads" x1="0" y1="0" x2="0" y2="1">
            <stop offset="5%" stopColor="#3B82F6" stopOpacity={0.8} />
            <stop offset="95%" stopColor="#3B82F6" stopOpacity={0} />
          </linearGradient>
        </defs>
        <CartesianGrid strokeDasharray="3 3" />
        <XAxis dataKey="month" />
        <YAxis />
        <Tooltip />
        <Legend />
        <Area
          type="monotone"
          dataKey="downloads"
          stroke="#3B82F6"
          fillOpacity={1}
          fill="url(#colorDownloads)"
        />
        <Area
          type="monotone"
          dataKey="installs"
          stroke="#10B981"
          fillOpacity={0.3}
          fill="#10B981"
        />
      </AreaChart>
    </ResponsiveContainer>
  );
}
// Recharts — custom tooltip
const CustomTooltip = ({ active, payload, label }) => {
  if (!active || !payload?.length) return null;
  return (
    <div className="bg-white border rounded p-3 shadow">
      <p className="font-semibold">{label}</p>
      {payload.map((entry) => (
        <p key={entry.name} style={{ color: entry.color }}>
          {entry.name}: {entry.value.toLocaleString()}
        </p>
      ))}
    </div>
  );
};

<AreaChart data={data}>
  <Tooltip content={<CustomTooltip />} />
  {/* ... */}
</AreaChart>

Best for: React apps needing standard charts with React idioms, custom tooltips, and composability.


D3.js (Power Tool)

// D3 — data transformations (not just charts)
import * as d3 from 'd3';

// D3 scales — the core building block
const xScale = d3.scaleTime()
  .domain([new Date('2026-01-01'), new Date('2026-12-31')])
  .range([0, 800]);

const yScale = d3.scaleLinear()
  .domain([0, d3.max(data, d => d.value) ?? 0])
  .range([400, 0]);

// D3 line generator
const line = d3.line<DataPoint>()
  .x(d => xScale(d.date))
  .y(d => yScale(d.value))
  .curve(d3.curveMonotoneX);

// D3 in React — using refs
import { useEffect, useRef } from 'react';
import * as d3 from 'd3';

function D3LineChart({ data }) {
  const svgRef = useRef<SVGSVGElement>(null);

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

    const svg = d3.select(svgRef.current);
    svg.selectAll('*').remove(); // Clear on redraw

    const width = 800, height = 400;
    const margin = { top: 20, right: 30, bottom: 30, left: 40 };

    const x = d3.scaleUtc()
      .domain(d3.extent(data, d => d.date) as [Date, Date])
      .range([margin.left, width - margin.right]);

    const y = d3.scaleLinear()
      .domain([0, d3.max(data, d => d.value) ?? 0])
      .range([height - margin.bottom, margin.top]);

    const lineGen = d3.line<typeof data[0]>()
      .x(d => x(d.date))
      .y(d => y(d.value));

    svg.append('path')
      .datum(data)
      .attr('fill', 'none')
      .attr('stroke', '#3B82F6')
      .attr('stroke-width', 2)
      .attr('d', lineGen);

    // Axes
    svg.append('g')
      .attr('transform', `translate(0,${height - margin.bottom})`)
      .call(d3.axisBottom(x));

    svg.append('g')
      .attr('transform', `translate(${margin.left},0)`)
      .call(d3.axisLeft(y));

  }, [data]);

  return <svg ref={svgRef} width="100%" viewBox="0 0 800 400" />;
}

Best for: Custom, complex visualizations — force graphs, geographic maps, tree diagrams, anything not in a standard chart library.


Visx (Airbnb)

// Visx — low-level React primitives
import { LinePath } from '@visx/shape';
import { scaleLinear, scaleTime } from '@visx/scale';
import { extent, max } from '@visx/vendor/d3-array';
import { AxisBottom, AxisLeft } from '@visx/axis';
import { GridRows } from '@visx/grid';
import { Tooltip, useTooltip } from '@visx/tooltip';

function VxLineChart({ data, width = 800, height = 400 }) {
  const margin = { top: 20, right: 20, bottom: 40, left: 50 };
  const innerWidth = width - margin.left - margin.right;
  const innerHeight = height - margin.top - margin.bottom;

  const xScale = scaleTime({
    domain: extent(data, d => d.date) as [Date, Date],
    range: [0, innerWidth],
  });

  const yScale = scaleLinear({
    domain: [0, max(data, d => d.value) ?? 0],
    range: [innerHeight, 0],
  });

  return (
    <svg width={width} height={height}>
      <g transform={`translate(${margin.left},${margin.top})`}>
        <GridRows scale={yScale} width={innerWidth} strokeDasharray="2,3" />
        <LinePath
          data={data}
          x={d => xScale(d.date)}
          y={d => yScale(d.value)}
          stroke="#3B82F6"
          strokeWidth={2}
        />
        <AxisBottom top={innerHeight} scale={xScale} />
        <AxisLeft scale={yScale} />
      </g>
    </svg>
  );
}

Best for: React teams who want D3's power with React's component model. Steeper learning curve than Recharts but more control.


Comparison Table

LibraryDownloadsBundleApproachLearning CurveBest Use Case
Chart.js10M~200KBConfig-drivenEasyQuick dashboards
D3.js9M~500KBImperativeHardCustom viz
Recharts3M~300KBReact componentsMediumReact apps
ECharts2M~1MBConfig-drivenMediumData-heavy dashboards
Visx200K~50KB*React primitivesHardCustom React charts

*Visx is modular — pay only for what you use.


When to Choose

ScenarioPick
Quick dashboard, any frameworkChart.js
React app with standard chartsRecharts
Custom, complex visualizationD3.js
React app needing D3 powerVisx
Mobile performance mattersECharts
Heatmaps, geographic vizD3.js
Financial/candlestick chartslightweight-charts (TradingView)
SSR with no canvas APIRecharts or Visx (SVG-based)

Compare charting library package health on PkgPulse.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.