The Best JavaScript Chart Libraries for 2026
Compare the best JavaScript chart libraries — D3, Chart.js, Apache ECharts, Highcharts, ApexCharts, Plotly.js, amCharts — by performance, bundle size, and use case. Updated May 2026 with verified npm and GitHub stats.
.png)
Key Takeaways
- Chart.js is the default JavaScript chart library in 2026 for production codebases — 10.4M weekly npm downloads, ~92 kB gzipped core that fits inside almost any perf budget, MIT-licensed, and 10 built-in chart types plus a deep plugin ecosystem (
chartjs-plugin-decimation,chartjs-plugin-zoom,chartjs-plugin-a11y). - Apache ECharts wins for scale and exotic chart types — canvas-by-default, tree-shakeable to ~100 kB gzipped, 600+ geo maps via the geo registry, comfortable with 100k+ points via built-in
series.sampling: 'lttb'downsampling. Battle-tested at Alibaba, Tencent, and Pinterest. - D3.js is the right tool for bespoke visualizations in mature engineering teams — 11.6M weekly DLs and 112.8k stars make it the most-installed JS visualization library, but it's a toolkit, not a chart library. Reach for it when no off-the-shelf chart matches the design and you have a senior frontend engineer who will own the custom drawing code through framework upgrades.
- Highcharts is the WCAG 2.1 AA pick — best accessibility story in the comparison (screen reader, sonification, keyboard nav out of the box) and stability since 2009, used in production at Goldman Sachs and across financial services. Commercial license required for for-profit apps.
- ApexCharts and amCharts 5 sit in the middle of the bundle-vs-polish trade-off — modern SVG defaults, strong out-of-the-box interactivity, smaller community than Chart.js. amCharts 5 has a mixed OSS/commercial licensing model worth checking before adoption.
- A standalone chart library is one decision out of many in a customer-facing analytics build — multi-tenant data isolation, RLS, embed auth, white-labeling, and AI insight surfacing typically dominate the engineering budget. The chart library is rarely the bottleneck.
This guide is for engineers picking a JavaScript chart library for a production codebase — the one you'll still be running in three years. We're optimizing for bundle budget at the 95th-percentile tenant, multi-tenant data scale, accessibility, RSC / SSR integration, and long-term maintenance signal — not for whichever library has the gentlest tutorial.
Picking a JavaScript chart library in 2026 comes down to three orthogonal choices: SVG vs Canvas vs WebGL (rendering engine, which sets your performance ceiling), framework-agnostic vs framework-bound (does it ship with Vue / Angular / Svelte adapters or is it React-only), and MIT / Apache 2.0 vs commercial license (which decides whether you can ship without paying a vendor). Almost every wrong call you can make on this decision is rooted in one of those three.
This guide covers the JavaScript-general chart libraries — the ones that work in vanilla JS, Vue, Angular, Svelte, plain HTML pages, or any framework you can mount a <div> in. If you're React-only and shipping inside a Next.js or Vite monorepo, see our React chart libraries guide for Recharts, Visx, Nivo, Victory, and MUI X Charts. If you're charting from a Python backend or a data-team-owned dashboard, the Python dashboard guide covers Plotly, Bokeh, Altair, and pyecharts.
What you'll find below: a side-by-side comparison table with verified May 2026 npm and GitHub numbers, deep dives with install + code snippets and explicit production trade-offs per library (bundle behavior, TypeScript ergonomics, SSR / RSC posture, WCAG 2.1 AA support, license terms, maintenance signal, named production users), a decision framework for the situation you're actually in, and a frank section on when a chart library stops being enough — and an embedded analytics platform takes over.
Quick Comparison Table
| Library | Renderer | Weekly DLs (May 2026) | GH stars | Last commit | Bundle (min / gz) | Best for |
|---|---|---|---|---|---|---|
D3.js (d3) | SVG (any DOM) | 11.6M | 112.8k | 2025-12-02 | ~280 kB / ~85 kB full; d3-scale ~12 kB gz alone | Bespoke visualizations, custom interactions |
| Chart.js | Canvas | 10.4M | 67.4k | 2026-04-16 | ~298 kB / ~92 kB | Default for standard charts under ~10k points |
| Apache ECharts | Canvas / SVG | 2.6M | 66.3k | 2026-05-02 | ~1.8 MB / ~520 kB full; ~100 kB gz tree-shaken | Massive datasets, geo, sankey, treemap |
| Highcharts | SVG | 2.3M | 12.4k | 2026-05-02 | ~485 kB / ~135 kB (commercial license) | Polished commercial dashboards, a11y |
| ApexCharts | SVG | 1.7M | active | active | ~440 kB / ~135 kB | Out-of-the-box zoom / brush / annotations |
| Plotly.js | SVG / WebGL | 891K | 18.2k | 2026-05-01 | ~3.2 MB / ~870 kB full; strict-mode ~430 kB gz | Scientific, statistical, 3D charts |
amCharts 5 (@amcharts/amcharts5) | Canvas / SVG | 255K | proprietary OSS hybrid | 2026-04-16 | ~480 kB / ~140 kB core (modular) | Maps, animated dashboards, branded UX |
Billboard.js (billboard.js) | SVG | 33K | 6.0k | 2026-04-18 | ~155 kB / ~45 kB (depends on D3) | Drop-in C3.js replacement, D3-based |
Stats verified 2026-05-02 from npm (api.npmjs.org), GitHub, and Bundlephobia. D3's
d3umbrella package is rarely used in production — most teams import individuald3-modules (d3-scale,d3-selection,d3-shape) and ship a fraction of the full bundle.*
A note on libraries we mention but don't deep-dive: FusionCharts (~35K weekly DLs, commercial, declining mindshare), SciChart (commercial, niche real-time / WebGL), Google Charts (last release 2014, not recommended for new projects).
How to choose the best JavaScript chart library for your use case
Before you pick a library, answer four questions. If you can't, you'll re-pick in three months.
- Dataset size at the 95th percentile of your customers? Under 5,000 points per chart: SVG is fine — Highcharts, ApexCharts, amCharts 5, Plotly.js. 5,000–100,000 points: switch to Canvas — Chart.js or Apache ECharts. 100,000+ or millions: ECharts with
series.sampling: 'lttb'/ WebGL, Plotly.js withscattergl, or specialized libraries (deck.gl for geo, regl for raw WebGL). Size for your real tenants, not your demo data. - Real-time update cadence? Canvas-rendered libraries (Chart.js, ECharts) re-render orders of magnitude faster than SVG when you push new data every second. SVG-based libraries (Highcharts, ApexCharts, amCharts 5 default mode) start stuttering past ~5 updates/sec on charts above 1,000 points — fine for a stock dashboard, not for a NOC monitoring view at 1Hz.
- Customization depth and design-system fit? Standard line/bar/pie/area/scatter that needs to inherit your design-system tokens: pick a high-level library with strong theming — Chart.js, Highcharts, or ApexCharts. Need a chord diagram with custom interactivity that no off-the-shelf chart matches: drop down to D3 directly. Sankey, treemap, calendar heatmap, geo with custom topojson: ECharts or amCharts 5.
- Framework / SSR constraints? Vanilla JS / multi-framework codebase: anything in this guide works. Vue, Angular, Svelte: Chart.js, ECharts, Highcharts, ApexCharts, and amCharts 5 all ship official or community wrappers. React: see the React chart libraries guide for the React-native options. Server-side rendering / Next.js App Router with React Server Components: Highcharts ships a Node renderer for true server-side image output; Chart.js, ECharts, ApexCharts, and Plotly.js all need a
'use client'boundary plusnext/dynamic({ ssr: false })to avoid hydration mismatches.
A fifth question worth asking before you npm install: license, accessibility, and maintenance signal. MIT or Apache 2.0 (Chart.js, ECharts, ApexCharts, D3, Plotly.js, Billboard.js) is fine for any commercial app — no questions to ask. Commercial (Highcharts, FusionCharts, SciChart) means a paid license. OSS-hybrid (amCharts 5) means free for non-commercial use, paid for commercial — worth checking before you adopt. WCAG 2.1 AA matters whenever your designer or a11y team plans to audit the charts: Highcharts ships it; ECharts has explicit aria config; Chart.js needs chartjs-plugin-a11y or manual ARIA wiring; D3 is whatever you build. Maintenance signal — last commit date, release cadence, CVE history, named-company production users — is the variable that decides whether you're still running this library in three years or yanking it out of a stalled dependency tree.
JavaScript chart library vs JavaScript visualization library — same thing?
Search engines treat "JavaScript chart library" and "JavaScript visualization library" as one query family, but there's a useful technical distinction. A chart library ships pre-built chart types (line, bar, pie, scatter, area, heatmap) with a high-level config-object or component API — Chart.js, ApexCharts, Highcharts, ECharts. A visualization library is lower-level — primitives for scales, axes, projections, and DOM/SVG/canvas drawing — that you compose into custom charts. D3.js is the canonical visualization library, not a chart library, even though it can draw any chart you want. In 2026, most teams use a chart library for the standard 90% of charts and reach for D3 (or Visx on React) for the bespoke 10%.
The 7 best JavaScript chart libraries — deep dives
Each section follows the same structure: what it is, performance, bundle size, customization (flexibility ceiling), TypeScript, DX, when to choose, when to skip — plus an install + minimal-chart code sample.
1. D3.js
D3 (d3, 11.6M weekly downloads, 112.8k GitHub stars, last commit 2025-12-02) is the most-installed JavaScript visualization library on npm. It's not a chart library — it's a low-level toolkit of selections, scales, projections, hierarchies, and data joins. Almost every other library in this guide either uses D3 internally (Billboard.js, Plotly.js partially) or borrows ideas from it.
npm install d3import * as d3 from "d3";
const data = [
{ month: "Jan", revenue: 4000 },
{ month: "Feb", revenue: 5200 },
{ month: "Mar", revenue: 6100 },
];
const width = 460, height = 240, margin = { top: 20, right: 20, bottom: 30, left: 50 };
const svg = d3.select("#chart").append("svg")
.attr("width", width).attr("height", height);
const x = d3.scalePoint().domain(data.map(d => d.month)).range([margin.left, width - margin.right]);
const y = d3.scaleLinear().domain([0, d3.max(data, d => d.revenue)]).nice()
.range([height - margin.bottom, margin.top]);
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));
svg.append("path")
.datum(data)
.attr("fill", "none").attr("stroke", "#3b82f6").attr("stroke-width", 2)
.attr("d", d3.line().x(d => x(d.month)).y(d => y(d.revenue)));- What it is: A toolkit of ~30 modular packages (
d3-scale,d3-selection,d3-shape,d3-hierarchy,d3-geo,d3-force,d3-zoom...) for binding data to DOM and drawing visualizations. You write the SVG/Canvas yourself. - Performance: Whatever you build. D3 itself adds nothing — you control the render strategy. SVG-based by default; pair with Canvas for 10k+ points or with WebGL via regl for millions.
- Bundle size: The
d3umbrella is ~280 kB minified / ~85 kB gzipped. In production almost nobody ships this. Import surgically —d3-scaleis ~12 kB gzipped,d3-shape~10 kB,d3-selection~9 kB. A typical custom chart pulls 4–6 modules totaling ~30–40 kB gzipped. - Flexibility: Total. There's nothing you can't draw. The flip side: you draw it.
- TypeScript: First-party types via the
@types/d3packages (and per-module@types/d3-*for surgical imports). Mature. - DX: Steep. D3 is a graphics primitive set, not a chart library — you write a lot of code per chart, but you control everything. Error messages are terse (you're often debugging a missing axis or a flipped scale by reading SVG paths in DevTools). StackOverflow and Observable density is high — almost every D3 question has been asked and answered.
- Used by: Every major newsroom (NYT, FT, Bloomberg, FiveThirtyEight), Observable, Datadog (legacy custom chart code), and internal BI at most large tech companies.
- When to choose: Bespoke visualizations no off-the-shelf chart covers — custom radial layouts, force-directed graphs with custom interactivity, parallel coordinates with linked brushing, geo maps with custom topojson and animated transitions. Codebases where designers ship Figma specs you can't approximate with Chart.js / Highcharts defaults, and your team has a senior frontend engineer who'll own the custom drawing code through framework upgrades.
- When to skip: Standard line / bar / pie / scatter — building those in D3 is an order of magnitude more code than the equivalent Chart.js or Highcharts call. Skip if your team can't dedicate ongoing maintenance ownership of custom SVG/canvas code — D3 charts age into "no one wants to touch this" territory faster than config-object libraries. Skip if your monorepo is on Webpack 4 or older bundlers that can't tree-shake D3 v7's ESM-only modules cleanly.
2. Chart.js
Chart.js (10.4M weekly downloads, 67.4k stars, last commit 2026-04-16) is the most popular general-purpose web chart library. Framework-agnostic, canvas-rendered, MIT-licensed, with an enormous plugin ecosystem.
npm install chart.jsimport {
Chart, LineController, LineElement, PointElement,
LinearScale, CategoryScale, Tooltip, Legend
} from "chart.js";
Chart.register(LineController, LineElement, PointElement, LinearScale, CategoryScale, Tooltip, Legend);
const ctx = document.getElementById("revenue-chart");
new Chart(ctx, {
type: "line",
data: {
labels: ["Jan", "Feb", "Mar"],
datasets: [{ label: "Revenue", data: [4000, 5200, 6100], borderColor: "#3b82f6", tension: 0.3 }],
},
options: { responsive: true, plugins: { legend: { position: "top" } } },
});- What it is: Canvas-rendered charts with a configuration-object API. Chart.js v4 ships 10 built-in chart types (line, bar, radar, doughnut, pie, polar area, bubble, scatter, area, mixed) plus a large plugin ecosystem (
chartjs-plugin-zoom,chartjs-plugin-annotation,chartjs-plugin-datalabels,chartjs-plugin-decimation). - Performance: Out of the box, comfortable to ~10–20k points before noticeable lag. With
chartjs-plugin-decimation(LTTB or min/max sampling, ships in core since v3) Chart.js handles 1M+ points by downsampling on render. Real-time dashboards pushing data every 250ms are comfortable on canvas. - Bundle size: ~298 kB minified / ~92 kB gzipped. Tree-shakeable since v3 — you must register the controllers and elements you use, as in the snippet above.
- Flexibility: Good for the standard chart types — line, bar, pie, area, scatter — and plugin-based for everything else. The tooltip, legend, and axis APIs are all replaceable; if you need a chart shape Chart.js doesn't ship, you're either writing a plugin or switching libraries.
- TypeScript: First-party types, mature. The deep options-object types occasionally feel busy in IntelliSense, but no
@types/*divergence and noanyleaks. - DX: Friendly.
new Chart(ctx, { type, data, options })and you have a chart — runnable example in five minutes. Error messages are clear ("X" is not a registered controller. You must register it first.) and StackOverflow density is the highest of any library in this comparison. - Used by: GitHub's contribution graph (per the brief's example), most Rails / Laravel admin dashboards, Stripe Atlas, GitLab metrics views, and a long tail of SaaS dashboards.
- When to choose: Default for any production JavaScript dashboard — line, bar, pie, area, scatter — under 50k points per chart, with or without real-time updates. MIT-licensed, no questions to ask. Best community-to-power ratio in the JS ecosystem.
- When to skip: When you need a chart Chart.js doesn't ship — sankey, geo, treemap, sunburst, calendar (use ECharts or amCharts 5). When you're React-only and want JSX-native composition (use Recharts). Skip if your designer or a11y team is going to audit the charts against WCAG 2.1 AA — Chart.js needs
chartjs-plugin-a11yplus manual ARIA wiring, and even then it doesn't match Highcharts' built-in screen-reader and sonification support. Skip if you need true server-side image rendering for scheduled PDF / email reports without spinning up a headless Chromium — Chart.js requires the DOM and won't run in a Node-only worker.
3. Apache ECharts
Apache ECharts (66.3k stars, 2.6M weekly DLs, last commit 2026-05-02) is the heaviest hitter in this comparison. Originally Baidu, donated to Apache, used in production by Alibaba, Tencent, and a long tail of enterprise BI tools. Canvas-by-default with an SVG renderer for vector exports, the widest chart-type catalog of any open-source option, and 600+ geo maps available via the geo registry.
npm install echartsimport * as echarts from "echarts/core";
import { LineChart } from "echarts/charts";
import { GridComponent, TooltipComponent, TitleComponent } from "echarts/components";
import { CanvasRenderer } from "echarts/renderers";
echarts.use([LineChart, GridComponent, TooltipComponent, TitleComponent, CanvasRenderer]);
const chart = echarts.init(document.getElementById("revenue-chart"));
chart.setOption({
title: { text: "Revenue by month" },
tooltip: { trigger: "axis" },
xAxis: { type: "category", data: ["Jan", "Feb", "Mar"] },
yAxis: { type: "value" },
series: [{ data: [4000, 5200, 6100], type: "line", smooth: true }],
});- What it is: Canvas/SVG dual-renderer with the widest catalog — line, bar, pie, scatter, candlestick, sankey, treemap, sunburst, calendar heatmap, parallel coordinates, gauge, geo, and more. 600+ pre-built geo maps available through the geo registry; built-in
datasetcomponent for relational data binding; built-in WebGL extension for 3D / large-data scenarios viaecharts-gl. - Performance: Fastest renderer in this comparison for very large datasets. Canvas-by-default is already faster than any SVG library, and
series.sampling: 'lttb'downsamples server-side so 100k+ point line charts render in milliseconds. With WebGL on, ECharts handles millions of points. If your dashboard's dataset depends on tenant — i.e. enterprise customers can have 10× more data than starter customers — see our guide on the build-vs-buy boundary for multi-tenant embedded analytics in React for the pattern that decouples chart rendering from query-time row counts. - Bundle size: ~1.8 MB minified / ~520 kB gzipped if you import the full bundle (
import * from 'echarts'). With proper tree-shaking — registering only the chart types and components you use, as in the snippet above — you can get down to ~100 kB gzipped. The "ECharts is heavy" reputation is real; the mitigation is also real. - Flexibility: Highest ceiling in the comparison — almost every visual detail is a configurable option, and the option object is well-documented. If you can describe it, ECharts can probably render it.
- TypeScript: Types ship with the package; the option object's depth makes IntelliSense feel busy, but the types are accurate and there's no
@types/*divergence. - DX: Mid. The option-object API is powerful but verbose, and error messages can be opaque (a typo in
series[0].typemay silently render an empty chart rather than throwing). Once you've written a couple of charts the pattern is consistent. Documentation is excellent and bilingual (Chinese / English). - Used by: Alibaba (origin), Tencent, Baidu, Pinterest, AWS Console (some metrics views), and a long tail of enterprise BI tools.
- When to choose: 50k+ points per chart at the 95th-percentile tenant, real-time monitoring at high tick rates, geo / sankey / calendar / treemap / sunburst, customer-facing analytics where the largest tenants will outscale anything else in this comparison. Apache 2.0 license. Pick this when no other library scales.
- When to skip: Skip when your bundle budget for the chart layer is below ~150 kB gzipped — even with aggressive tree-shaking, ECharts' option-object surface area carries weight you don't fully recover, and your perf review will fail. Skip when the team is unfamiliar with imperative configuration-object APIs and code review will be slow on every chart change. Skip when your dashboard's largest tenant is 2k points and Chart.js gets you the same chart in half the code.
4. Highcharts
Highcharts (2.3M weekly DLs, 12.4k stars, last commit 2026-05-02) is the polished commercial pick. Best accessibility story in this comparison (WCAG 2.1 AA out of the box, screen-reader support, sonification module), and stability — Highcharts has been shipping continuously since 2009 and the API has evolved without breaking changes that bite production teams.
npm install highchartsimport Highcharts from "highcharts";
Highcharts.chart("revenue-chart", {
title: { text: "Revenue by month" },
xAxis: { categories: ["Jan", "Feb", "Mar"] },
yAxis: { title: { text: "Revenue ($)" } },
series: [{ type: "line", name: "Revenue", data: [4000, 5200, 6100] }],
});- What it is: SVG-based commercial chart library with first-party Highstock (financial), Highmaps (geo), and Gantt modules. Accessibility, exporting (PNG/SVG/PDF/CSV), and theming are first-class.
- Performance: SVG, comfortable to ~10k points. Highcharts'
boostmodule switches to WebGL for series above ~10k points and handles millions, similar to ECharts' WebGL path. - Bundle size: ~485 kB minified / ~135 kB gzipped for Highcharts core.
- Flexibility: Excellent. The API has matured for over a decade and feels more polished than Chart.js or ECharts in the small details — tooltip positioning, legend wrapping, axis labels, animation easing, and theming through
Highcharts.setOptionsare all first-class. - TypeScript: First-party types, mature. Cleaner ergonomics than Chart.js or ECharts on deeply nested config.
- DX: Friendly.
Highcharts.chart(container, options)and you have a chart. Error messages are useful, the documentation is the most thorough of any library in this comparison, and the demo gallery is genuinely useful as a copy-paste reference. - Used by: Goldman Sachs, Twitter (now X), NASA reporting tools.
- When to choose: Customer-facing dashboards where your designer or a11y team is going to audit the charts against WCAG 2.1 AA — Highcharts is the only library in this comparison that ships full screen-reader, sonification, and keyboard-nav support out of the box. Financial / stock charts (Highstock). Apps where API polish and 15+ years of stability matter more than license cost.
- When to skip: Skip when your stack is fully OSS and you'd rather not pay a vendor — Chart.js, ECharts, and ApexCharts cover most of the same ground. Skip when budget for a commercial chart library isn't justified by the use case. Note: Highcharts pricing varies by use case (commercial, OEM, embedded) and changes over time — check the current Highcharts license terms before committing rather than relying on numbers from older blog posts.
5. ApexCharts
ApexCharts (1.7M weekly DLs, actively maintained) is a modern open-source SVG chart library with the strongest out-of-the-box interactivity in the comparison — range zoom, brushing across charts, annotations, animated transitions, and PNG/SVG/CSV exports all work without extra plugins.
npm install apexchartsimport ApexCharts from "apexcharts";
const options = {
chart: { type: "line", zoom: { enabled: true }, toolbar: { show: true } },
series: [{ name: "Revenue", data: [4000, 5200, 6100] }],
xaxis: { categories: ["Jan", "Feb", "Mar"] },
stroke: { curve: "smooth", width: 2 },
title: { text: "Revenue by month" },
};
const chart = new ApexCharts(document.getElementById("revenue-chart"), options);
chart.render();- What it is: SVG-based interactive charts with rich built-in UX. Configuration-object style similar to Chart.js but with much more shipped-in interactivity.
- Performance: SVG, comfortable to ~10k points. For larger datasets switch to Chart.js or ECharts.
- Bundle size: ~440 kB minified / ~135 kB gzipped. Heavier than Chart.js core, lighter than ECharts.
- Flexibility: Strong on the interactivity axis — zoom, brush, annotations are configuration toggles, not custom plugins. Theming is rich (light/dark, custom palettes, monochrome). Less flexible than ECharts on exotic chart shapes.
- TypeScript: First-party types.
- DX: Friendly. Declarative options object, runnable example in five minutes, useful demo gallery. The interactivity defaults are the closest thing in OSS to "looks great without theming."
- Used by: Broad SMB SaaS adoption, particularly in newer dashboards built since 2022 — but with weaker brand-name signal than Chart.js or ECharts.
- When to choose: Customer-facing dashboards where the out-of-the-box interactivity (range zoom on a time series, brushing, annotations, animated transitions) saves you implementation time you'd otherwise spend writing custom plugins. SVG visual quality matters, the 95th-percentile tenant is under 10k points, and your design team wants polished interactivity without a six-week build.
- When to skip: Skip when the 95th-percentile tenant exceeds ~10k points per chart — ApexCharts' SVG renderer will become the bottleneck before the network does (use Chart.js or ECharts instead). Skip when your a11y team is going to audit the charts — ApexCharts' a11y story is mid (basic ARIA on tooltips, gaps on screen-reader narration); prefer Highcharts or ECharts with explicit
ariaconfig. Skip when SSR cleanliness matters — ApexCharts requires a'use client'+next/dynamic({ ssr: false })boundary or you'll see hydration mismatch errors on every paint.
6. Plotly.js
Plotly.js (891K weekly DLs, 18.2k stars, last commit 2026-05-01) is the JavaScript engine behind Python's Plotly and R's Plotly. The strongest pick for scientific, statistical, and 3D charts — box plots, violin plots, contour plots, 3D surfaces, and WebGL-accelerated scattergl for million-point scatter plots are all first-class.
npm install plotly.js-distimport Plotly from "plotly.js-dist";
Plotly.newPlot("revenue-chart", [{
x: ["Jan", "Feb", "Mar"],
y: [4000, 5200, 6100],
type: "scatter",
mode: "lines+markers",
line: { color: "#3b82f6" },
name: "Revenue",
}], {
title: "Revenue by month",
xaxis: { title: "Month" },
yaxis: { title: "Revenue ($)" },
});- What it is: Scientific-grade interactive charts — 40+ chart types including 3D, contour, parallel coordinates, ternary, and geo. Backed by D3 (for layout) and WebGL (for
scattergl,scatter3d,surface,mesh3d). - Performance: SVG by default — comfortable up to ~10k points. Switch trace
typetoscatterglfor hundreds of thousands of points; usescatter3dfor 3D with WebGL acceleration. - Bundle size: Plotly's reputation for size is earned. The full
plotly.js-distbuild is ~3.2 MB minified / ~870 kB gzipped. The strict-mode build (plotly.js-dist-minwith thestrictpartial bundle) drops unused chart types and lands at ~1.5 MB / ~430 kB gzipped. Custom partial bundles (e.g.plotly.js/lib/index-cartesian) get smaller still. - Flexibility: Strong on chart-type breadth (40+ including statistical and 3D), mid on layout/theming polish — the look-and-feel skews "scientific paper" by default and takes work to brand.
- TypeScript: First-party types via
@types/plotly.js. - DX: Mid. The
Plotly.newPlot(div, traces, layout)pattern is straightforward but the trace and layout options are deep, and partial bundles add a configuration step before you can ship. Documentation is thorough but skews toward Python and R audiences. - Used by: NASA mission dashboards, JPMorgan, scientific publishers, anywhere with Python Dash dashboards.
- When to choose: Scientific, financial, or statistical visualizations — box, violin, contour, ternary, parallel coordinates, 3D surfaces. Apps already invested in the Plotly ecosystem (Python Dash, Plotly Express).
- When to skip: Skip when your bundle budget for the chart layer is under ~200 kB gzipped — even Plotly's strict-mode build at ~430 kB gzipped consumes most of a 200–400 kB-gzipped front-end budget on its own and your perf review will fail. Skip when anyone running
npm auditwill flag the surface area: Plotly.js depends on more transitive packages (3D, geo, mapbox, financial) than ECharts or Chart.js, which is more dependencies to keep an eye on. Skip for standard SaaS dashboards where the chart types are line / bar / pie / area — Chart.js gets you the same chart at a quarter of the bundle.
7. amCharts 5
amCharts 5 (@amcharts/amcharts5, 255K weekly DLs, last commit 2026-04-16) is a TypeScript-first chart library with a focus on maps, animated transitions, and branded-UX dashboards. Modular packages — install only the pieces you use.
npm install @amcharts/amcharts5import * as am5 from "@amcharts/amcharts5";
import * as am5xy from "@amcharts/amcharts5/xy";
import am5themes_Animated from "@amcharts/amcharts5/themes/Animated";
const root = am5.Root.new("revenue-chart");
root.setThemes([am5themes_Animated.new(root)]);
const chart = root.container.children.push(am5xy.XYChart.new(root, {}));
const xAxis = chart.xAxes.push(am5xy.CategoryAxis.new(root, {
categoryField: "month",
renderer: am5xy.AxisRendererX.new(root, {}),
}));
const yAxis = chart.yAxes.push(am5xy.ValueAxis.new(root, {
renderer: am5xy.AxisRendererY.new(root, {}),
}));
const series = chart.series.push(am5xy.LineSeries.new(root, {
xAxis, yAxis,
valueYField: "revenue",
categoryXField: "month",
}));
const data = [{ month: "Jan", revenue: 4000 }, { month: "Feb", revenue: 5200 }, { month: "Mar", revenue: 6100 }];
xAxis.data.setAll(data);
series.data.setAll(data);- What it is: Canvas-rendered (with SVG export) charts and maps. amCharts has the strongest Maps module of any open-source-friendly library — country-level, region-level, and custom topojson maps with animated zoom and pan.
- Performance: Canvas-by-default with smooth 60 fps animations. Comfortable to 50k+ points; uses lazy rendering for off-screen series.
- Bundle size: ~480 kB minified / ~140 kB gzipped for the core; modular packages mean you only pay for the chart types you load.
- Flexibility: Strong theming and animation primitives. Builder-style composition (
am5xy.XYChart.new(root, {...}).children.push(...)) gives finer control over each visual element than Chart.js or ApexCharts, but at the cost of more code per chart. - TypeScript: First-party — amCharts 5 was rewritten in TypeScript, and the type ergonomics are among the cleanest of any library here.
- DX: Verbose. The builder-style API is more code per chart than Chart.js or ApexCharts; engineers used to declarative options objects will write more lines to get to the first chart. Documentation and examples are good once you internalize the pattern.
- Used by: Less brand-name signal than Chart.js or ECharts; common in enterprise BI internal tools and dashboards with heavy map / geo requirements.
- When to choose: Dashboards with animated transitions, branded UX, or strong map requirements (country drill-downs, custom topojson, choropleths). Customer-facing analytics where geo / map fidelity is a product differentiator.
- When to skip: Skip when your project is commercial and you'd rather not check whether a hybrid OSS / commercial license fits your use case — Chart.js or ECharts are fully MIT/Apache, no questions to ask. Skip when your bundle budget for the chart layer is under ~140 kB gzipped — amCharts 5 starts there at the core and grows with each chart-type module. Skip when the verbose
am5.X.new(root, {...})builder-style API will slow code review on every chart change.
Honorable mentions / niche tools
- Billboard.js (33K weekly DLs, 6.0k stars, last commit 2026-04-18) — D3-based SVG chart library, drop-in successor to the (now archived) C3.js. Lighter than Chart.js, useful when you want C3's simple API without C3's stale dependencies. Pulls in D3 modules.
- FusionCharts (~35K weekly DLs) — commercial, broad chart catalog, declining open-source mindshare. Brief mention only; most teams pick Highcharts or ECharts in the same use case today.
- SciChart — commercial, niche real-time / WebGL chart library for high-frequency streaming data (trading, scientific instrumentation). Excellent at what it does; commercial license, contact for pricing.
- Google Charts — last release 2014, no active development. Not recommended for new projects in 2026, despite still appearing in older "best of" lists.
If you're React-only, the ergonomics shift — see our React chart libraries guide for Recharts, Visx, Nivo, Victory, MUI X Charts, and the React-flavored wrappers around Chart.js / ECharts / ApexCharts. If you're charting from a Python backend, see the Python dashboard guide.
Decision framework — which one when
| Situation | Pick |
|---|---|
| Standard line / bar / pie / scatter, under 10k points | Chart.js |
| Real-time monitoring, 10k+ points / sec | Chart.js (with decimation) or Apache ECharts |
| Massive datasets (100k+), geo, sankey, calendar, treemap, sunburst | Apache ECharts |
| Bespoke custom visualizations no library covers | D3.js |
| Customer-facing polished interactivity (zoom, brush, annotations) | ApexCharts or Highcharts |
| Accessibility-first (WCAG 2.1 AA out of the box), financial / stock charts | Highcharts |
| Scientific, statistical, or 3D charts | Plotly.js |
| Heavy map / geo work, animated branded dashboards | amCharts 5 |
| Drop-in C3.js replacement | Billboard.js |
| You're stacking five chart libraries plus per-tenant query glue | Stop. Embed analytics. |
Common pitfalls developers hit
A handful of production-scale issues bite engineering teams no matter which chart library they pick. Hit a couple of these, lose a sprint — sometimes lose a release.
- D3 v7 ESM-only imports break older Webpack 4 monorepos — D3 v7 dropped CJS in favor of ESM-only modules. Older monorepos still on Webpack 4 (or any bundler that hasn't migrated to native ESM) will fail with
ERR_REQUIRE_ESMat build time. Either upgrade your bundler, pin D3 to v6 (last CJS-compatible release), or add a transpile rule fornode_modules/d3-. Also:import as d3 from "d3"ships the full ~85 kB gzipped umbrella — switch to per-module imports (import { scaleLinear } from "d3-scale") and the bundle drops to ~10–30 kB gzipped, which is usually the difference between passing and failing your perf review. - Chart.js + Next.js App Router —
'use client'+next/dynamic({ ssr: false })for hydration safety — Chart.js touches the DOM; rendering during server rendering throwswindow is not defined. Wrap every Chart.js component file with'use client'on the App Router, or usenext/dynamicwithssr: falsefor the Pages Router. Same pattern applies to ApexCharts and ECharts. Forgetting this in a customer-facing analytics build surfaces as "the charts work locally but the production deploy fails" — a classic Friday-evening incident. - ECharts tree-shaking only works through the modular import path —
import * as echarts from "echarts"ships the full 1.8 MB minified / 520 kB gzipped bundle and torches your perf budget. You must use the modular import path (echarts/core+ explicitecharts.use([...])registration of the chart types, components, and renderer you actually use, as in the deep-dive snippet) to tree-shake down to ~100 kB gzipped. The "ECharts is heavy" complaint in postmortems almost always traces back to this one line. - Highcharts license-key warning leaks to the browser console in production if you forget the build flag — Highcharts ships fine without a license but renders a "Highcharts.com" watermark and emits a console warning on every page load. In production you must set the build flag (
Highcharts.setOptions({ credits: { enabled: false } })) and wire the paid license key — otherwise your customers see the watermark, your security team sees the console warning in their browser audit, and you get pinged about why third-party telemetry is leaking from a paid library. Don't ship a Highcharts dashboard until license + key are wired and verified in a staging build. - Plotly.js is 3 MB before tree-shaking — use the strict-mode build or split it out of the main bundle entirely —
plotly.js-distis ~3.2 MB minified / ~870 kB gzipped, which is multi-second download on a 3G connection and an instant fail on a Lighthouse audit. Useplotly.js-dist-minwith the strict-mode partial bundle (~430 kB gzipped) for cartesian-only use cases, or build a custom partial bundle with only the chart types you ship. If Plotly is only used in one route, code-split it out of your main entry chunk so the rest of the app doesn't pay the bundle cost. - ApexCharts SSR hydration mismatch on randomly-generated SVG IDs — ApexCharts renders SVG that includes randomly-generated IDs, which differ between server and client and trigger React's hydration mismatch error on first paint. Use the same
'use client'+next/dynamic({ ssr: false })pattern as Chart.js, or pin IDs via the chart options. The same class of bug bites amCharts 5 if you don't gate it behind a client-only mount.
When to stop using a chart library
At a typical SaaS dashboard scale, the engineering split is roughly 75% chart library work + 25% glue code — picking the right chart, theming it, plumbing data in. Once you cross 50+ tenants or need row-level security at query time, that ratio inverts to ~25% charts + ~75% platform plumbing — and that's the boundary where a chart library alone stops being viable. The chart library still draws the chart, but you're now also building:
- Multi-tenant query isolation and row-level security
- Per-tenant theming and white-labeling
- Embed authentication (signed JWTs, guest tokens, expiring URLs)
- Caching and query pre-aggregations
- A semantic / metric layer so the same KPI definition powers every chart
- Dashboard layout, filters, drill-downs, and cross-filtering
- Scheduled exports (PDF, CSV, email)
- AI insight surfacing — natural-language Q&A over the same data
- An admin UI for non-engineers to build dashboards
- API rate limiting and per-tenant quotas
None of that is what a chart library does. All of it is what an embedded analytics platform — Databrain, Cube, Metabase, or similar — does out of the box. If your dashboard is internal (used by your own team), keep stacking chart libraries; the glue code cost is amortized over a small user surface. If your dashboard is customer-facing inside a SaaS product, the math flips — almost every team we talk to that started with Chart.js + ECharts + custom RBAC eventually rebuilds against an embedded analytics SDK.
See our embedded analytics tools comparison for the platform side-by-side, and Embedded Analytics in React for the React implementation pattern (guest tokens, RLS, white-labeling).
Conclusion
If you remember three things from this guide:
- Default to Chart.js for standard production JavaScript dashboards — line, bar, pie, area, scatter — under ~10k points per chart. The config-object API is straightforward to code-review, the ~92 kB gzipped core fits inside almost any perf budget, the plugin ecosystem covers most asks, the MIT license has no strings, and the maintenance signal is strong.
- Reach for Apache ECharts the moment you cross the canvas ceiling — 50k+ points at the 95th-percentile tenant, real-time monitoring at high tick rates, geo / sankey / calendar / treemap / sunburst, or large-scale BI. Apache 2.0 license, in production at Alibaba and Pinterest. It's the canvas-rendered escape hatch with the widest chart catalog.
- Reach for D3 when no chart library covers the design and you have a senior frontend engineer to own the custom drawing code. Bespoke radial layouts, force-directed graphs, parallel coordinates with linked brushing, anything that has to match a Figma spec the off-the-shelf libraries can't approximate.
Everything else is situational: Highcharts when WCAG 2.1 AA is a hard requirement and a paid license is fine; ApexCharts for out-of-the-box interactivity (range zoom, brushing, annotations); Plotly.js for scientific / financial / 3D visualizations; amCharts 5 for heavy map/geo or branded animated UX; Billboard.js as a lightweight C3 replacement.
And the meta-recommendation: the best JavaScript chart library is the one you don't have to grow into a small embedded analytics platform. If your roadmap includes multi-tenancy, row-level security, embed auth, white-labeling, scheduled PDF/email exports, AI insight surfacing, and self-serve dashboard builders, that work doesn't get cheaper by stacking more chart libraries — it gets cheaper by embedding a platform that ships those primitives and using the chart library for the 5% of charts the platform doesn't already render. See our embedded analytics tools comparison for the platform side-by-side, Embedded Data Visualization for the architectural deep dive on when chart libraries stop scaling, and Embedded Analytics in React for the multi-tenant guest-token implementation pattern.
Next Steps
- Build a dashboard from scratch (React): Create a React Dashboard — The Complete 2026 Guide — runnable Recharts + Chart.js walkthrough that doubles as a JavaScript chart libraries beyond React reference
- Framework-specific deep dive: The Best React Chart Libraries for 2026 — Recharts, Visx, Nivo, Victory, MUI X
- Cross-language reference: Python Dashboard: The Complete 2026 Guide — Plotly, Matplotlib, Bokeh, Altair, pyecharts
- Embedded analytics tools: Embedded Analytics Tools Comparison — when chart libraries stop scaling
- Customer-facing analytics architecture: Embedded Analytics in React — the multi-tenant guest-token pattern, RLS, white-labeling
Covers D3 7.x, Chart.js 4.x, Apache ECharts 6.x, Highcharts 12.x, ApexCharts 4.x, Plotly.js 3.x, amCharts 5.x, Billboard.js 3.x. Stats verified May 2, 2026. Last updated May 2026.
Vishnupriya is a Data Analyst at Databrain specializing in data visualization, SQL, Python, and data modeling.
Frequently Asked Questions
Are any of these libraries risky to ship in a production codebase?
Mostly no, but a few things are worth checking before you add one to package.json. License: Chart.js, Apache ECharts, ApexCharts, Plotly.js, Billboard.js, and D3 are MIT or Apache 2.0 — fine for any commercial app. Highcharts, FusionCharts, and SciChart are commercial — you'll need a paid license for for-profit use. amCharts 5 is OSS-hybrid (free for non-commercial, paid for commercial) — worth a look before adopting. CVE history and npm audit posture: D3, Chart.js, and ECharts have clean recent CVE histories and well-resourced maintainer teams (Apache for ECharts, the Chart.js org for Chart.js). Plotly.js's surface area is broader (3D, geo, mapbox, financial) so npm audit will report more transitive dependencies — not necessarily worse, just more to keep an eye on. Maintenance signal: the libraries with active 2026 commits (Chart.js, ECharts, Highcharts, ApexCharts, Plotly.js, amCharts 5, Billboard.js) are all safe long-term bets. D3's last commit on the umbrella repo is 2025-12-02 — slower than the others but its modular packages still see frequent releases. The library to avoid for new projects is Google Charts (last release 2014, effectively dead).
Which JavaScript chart library handles accessibility best?
Highcharts is far ahead — it ships full screen-reader narration, keyboard navigation, sonification (auditory chart playback), and high-contrast themes out of the box. It's the only library in this comparison most teams pass a WCAG 2.1 AA audit with zero custom work. Apache ECharts ships an explicit aria config option that produces meaningful screen-reader output for series and data points, and is the strongest open-source option. Chart.js has no built-in a11y — you need chartjs-plugin-a11y plus manual ARIA wiring, and even then complex charts (multi-axis, interactive) require additional work. ApexCharts sits in the middle (basic ARIA on tooltips, gaps on screen-reader narration of series). D3 is whatever you build — fully accessible D3 charts are possible but rare in practice. If your designer or a11y team is going to audit the charts, default to Highcharts; if you're OSS-only, default to ECharts with explicit aria config.
How do JavaScript chart libraries handle SSR / hydration / RSC?
Mostly: badly, with workarounds. Chart.js is window-dependent and throws on import in a Node SSR context — wrap with 'use client' plus next/dynamic({ ssr: false }) on the Next.js App Router. ApexCharts has the same issue plus a hydration mismatch on randomly-generated SVG IDs — same workaround. ECharts can be server-rendered with care (it ships an SSR-safe entry point) but most teams gate it behind a client-only boundary anyway because the option-object surface is easier to debug client-side. Plotly.js is heavily window-dependent — strict client-only. D3 is server-safe if you stay declarative (compute the SVG path strings on the server, hydrate without re-running interactivity); D3's selection API is window-dependent. Highcharts is the cleanest SSR story — it ships a Node renderer (highcharts-export-server) for true server-side image generation, useful for scheduled PDF/email reports without spinning up a headless Chromium. For an RSC codebase, the practical answer is: every chart component lives behind a 'use client' boundary regardless of library, and you choose based on the rest of the trade-offs.
Which JavaScript chart library is best for large datasets at the 95th-percentile tenant?
Apache ECharts. Canvas-by-default, built-in series.sampling: 'lttb' downsampling that runs server-side or in a worker, optional WebGL via echarts-gl, tree-shakeable to ~100 kB gzipped, comfortable with 100k+ points per chart even on enterprise tenants 10× the size of your demo data. Chart.js with chartjs-plugin-decimation is the next-best option — 1M+ points possible with LTTB or min/max sampling, smaller learning curve, but the memory profile is heavier than ECharts above 100k. D3 with Canvas + a quadtree spatial index is the right answer when you need full control over hit-testing on a 1M-point scatter — most chart libraries fall over on point-precision interactivity at that scale. For 1M+ points without aggregation you're outside chart-library territory entirely — deck.gl (geo / WebGL), regl (raw WebGL), or upstream aggregation with Datashader-style tile servers.
Which JavaScript chart libraries are actually used in production at scale?
The five safe long-term bets in 2026, with publicly documented production users:
- Apache ECharts — Alibaba (origin) and Tencent commerce dashboards, plus broad adoption across Chinese tech. Apache foundation governance and the Alibaba / Baidu maintenance investment make it one of the most production-hardened OSS chart libraries on npm.
- D3 — every major newsroom (NYT, FT, Bloomberg, FiveThirtyEight, Reuters), Observable, and the foundation underneath countless custom dashboards in internal BI tooling at large tech companies.
- Highcharts — public customer references include NASA and broad adoption across financial-services dashboards. Pick this when WCAG 2.1 AA is a hard requirement and the commercial license is fine.
- Chart.js — bundled by default into most Rails and Laravel admin dashboard frameworks, ships in countless SaaS admin panels, and is the most-installed general JS chart library on npm at 10.4M weekly downloads. The
chartjs-plugin-decimationextension is what most teams reach for when datasets cross ~20k points. - Plotly.js — Plotly's own customer page lists scientific publishers, NASA, and financial-services teams. Almost any Python Dash dashboard you encounter is rendering Plotly.js underneath.
ApexCharts is widely adopted in newer SaaS dashboards but with weaker named-company signal than the five above. amCharts 5 is common in enterprise BI internal tools — used in production at scale, just not by companies that publish their stack on a marketing page. Production-user signal isn't the only thing that matters; equally important are active 2026 commit history, clean npm audit posture, and recognizable maintainer-team resourcing — all of which the seven libraries we deep-dive on currently pass.




