Choosing a JavaScript Charting Library in 2026: ECharts vs D3 vs Recharts vs Plotly vs Chart.js vs deck.gl
by Ridhwaan Mayet, Data Engineering Consultant
The real question
Every “best charting library” comparison I’ve read makes the same mistake: it reviews six libraries in isolation and declares a winner. There is no winner. There is only a trade-off, and it’s the same one every time: how much control do you need, versus how fast do you need to ship?
I’ve shipped charts with most of this list. Recharts and Plotly in client dashboards, D3 when nothing off the shelf fit, and deck.gl in production for the Decarb.Earth case study, a public solar-impact map rendering 16,200 irradiance cells inside a Next.js app. That last one is instructive precisely because none of the conventional charting libraries could have done it, and I’ll come back to why.
The point of this post is not six mini-reviews. It’s a framework: figure out where your product sits on three axes (API model, rendering performance, and long-term cost) and the choice usually makes itself.
Axis one: how the API thinks
The libraries split into three API philosophies, and the philosophy matters more than the feature list.
Config-driven chart objects. ECharts, Chart.js and Plotly all work the same way at heart: you hand the library a big declarative options object (data, axes, series types, tooltips) and it renders the whole chart. This is the fastest path from data to pixels. It’s also the easiest to serialise, template, and generate from a backend, which matters more than people expect in data products where chart definitions live in a database or come out of a query layer. The cost is that you’re working around your UI framework, not with it: the chart owns its DOM node, and React or Vue just hosts it.
React-component wrappers. Recharts inverts that. Charts are composed from JSX (<LineChart>, <XAxis>, <Tooltip>) so a chart is just another component in your tree. State, props, conditional rendering and your design system all work the way your team already expects. For product dashboards built by React teams, this is the lowest-friction option I know, and it’s my default recommendation for exactly that case. The trade-off is a ceiling: you get the charts Recharts ships, composed the way Recharts composes them.
The toolbox. D3 is not a charting library, and treating it like one is the most common mistake on this list. D3 is a collection of primitives (scales, shape generators, selections, forces, geo projections) from which you can build any visualisation. That’s its glory and its cost. A bespoke chart that would be impossible elsewhere is a good afternoon’s work in D3 if you know it; a basic bar chart is a bad afternoon’s work if you don’t. My honest advice: most teams should not build their charting stack on raw D3. Use it surgically (a custom scale here, a projection there, often inside another library’s escape hatches) and reach for the full toolbox only when the visualisation genuinely doesn’t exist off the shelf.
Top tip
Ask who will define charts in production. If chart specs come from configuration, a database, or an API, pick a config-driven library: a JSON-shaped options object is trivial to store and generate. If charts are hand-built by frontend engineers inside a React app, component wrappers will fit your codebase better.
Axis two: rendering tiers, and where each breaks
Performance conversations about charting libraries are usually vague. They don’t need to be, because the ceiling is mostly set by the rendering technology, not the library’s cleverness.
SVG puts every data point in the DOM. That gives you crisp output, easy styling, and accessible, inspectable markup. It degrades once you’re pushing thousands of nodes, especially with animation or frequent updates. Recharts is SVG. So is D3 in its most common usage. For typical product dashboards (dozens of series, hundreds of points), SVG is completely fine, and its debuggability is genuinely valuable.
Canvas draws pixels instead of managing nodes, so it comfortably handles tens to hundreds of thousands of points. Chart.js is canvas-native. ECharts defaults to canvas (with an SVG renderer available) and adds incremental and progressive rendering for large series, which is why it’s my usual answer when someone says “the dashboard got slow.” You lose per-element DOM access; hit-testing and accessibility take more work.
WebGL hands the drawing to the GPU and moves the ceiling into the millions of points. Plotly exposes this through its GL trace types for large scatters; deck.gl is WebGL from the ground up. This tier exists for a reason, but don’t start here: GPU pipelines bring their own complexity, and below roughly a hundred thousand points you’re paying that complexity for nothing.
The practical rule: match the tier to your worst realistic dataset, not your demo dataset. A chart that’s smooth with 500 points in staging and unusable with 80,000 in production is a rendering-tier mistake, not a tuning problem.
The special case: deck.gl
deck.gl deserves its own section because it isn’t competing with the others: it’s a layered WebGL framework for large-scale, usually geospatial, visualisation. You describe data as layers (GeoJSON, hexagons, point clouds, paths), it compiles them to GPU-friendly buffers, and it composites them over a basemap like MapLibre.
On the Decarb.Earth platform, the public Impact Potential Map renders a global grid of 16,200 solar-irradiance cells aggregated to country level, drawn with deck.gl 9 over MapLibre inside Next.js. One detail from that build generalises well: we split the render into a dual-layer setup: a non-interactive base layer for raw drawing speed, and a separate pickable layer for hover detail. Interactivity has a real cost in WebGL picking, and paying it only on the layer that needs it kept the map responsive. That kind of thinking (layers, buffers, picking passes) is exactly what tells you deck.gl is a different category of tool.
Choose it when your problem is genuinely spatial and genuinely large. Don’t choose it for a line chart, and don’t choose Recharts for a continental choropleth. The full write-up is in the Decarb.Earth case study.
Axis three: bundle size, licensing, maintenance
Three unglamorous factors that decide more projects than any feature comparison.
Bundle size. I won’t quote exact kilobyte figures (they shift with every release and every tree-shaking configuration) but the tiers are stable. Chart.js is the lightweight of the group. Recharts sits in the middle, plus React itself. ECharts is large by default but supports importing only the chart types and components you use, which changes the picture substantially. Full Plotly is the heavyweight (genuinely an order of magnitude beyond the light end) because it bundles an enormous scientific chart catalogue; partial bundles exist and you should use them. deck.gl is modular but you’re shipping a GPU framework, so budget accordingly. If your charts render on a public, SEO-relevant page, this axis moves up the priority list.
Licensing. As of writing, this is a quietly good story: everything here is permissively licensed (MIT for Chart.js, Recharts, Plotly.js and deck.gl, Apache-2.0 for ECharts, ISC for D3). No copyleft traps, no “open-core with the good charts behind a paywall.” Verify at adoption time, but licence risk should rarely be the deciding factor among these six.
Maintenance. The question to ask is “who is paying for this to exist?” ECharts is an Apache Software Foundation project. deck.gl sits under open governance in the vis.gl suite (under the OpenJS Foundation as of writing) with heavy industrial users. D3 and Plotly have long institutional histories. Chart.js and Recharts are healthy community projects with enormous install bases. All six are safe bets today; the discipline is re-checking commit activity and release cadence before you commit, not trusting a blog post, including this one.
Top tip
Whatever you pick, wrap it. A thin internal Chart component that owns theming, formatting and defaults means a future library migration touches one module instead of every dashboard screen. Every stack I’ve regretted skipped this step.
The summary table
| Library | API model | Rendering | Sweet spot | Breaks down when… |
|---|---|---|---|---|
| Chart.js | Config object | Canvas | Simple dashboards, small bundles | You need exotic chart types or fine control |
| Recharts | React components | SVG | React product dashboards | Point counts climb into the thousands |
| ECharts | Config object | Canvas (SVG optional) | Feature-rich dashboards, big-ish data | You need full custom visual grammar |
| Plotly | Config object | SVG + WebGL traces | Scientific/analytical charts, 3D | Bundle size matters and you skip partial builds |
| D3 | Low-level toolbox | SVG/Canvas (you decide) | Bespoke visualisations, one-off pieces | Used as a standard chart library by a team |
| deck.gl | Layer framework | WebGL | Large-scale geospatial, millions of points | The problem isn’t spatial or isn’t large |
Pick X if…
- Pick Chart.js if you need standard charts, fast, with the smallest footprint, and you don’t expect requirements to grow exotic.
- Pick Recharts if you’re a React team building a product dashboard and your data volumes are dashboard-sized. This is the pragmatic default for most SaaS work.
- Pick ECharts if you want the widest off-the-shelf chart catalogue with real large-data headroom, or your chart definitions are generated rather than hand-written.
- Pick Plotly if your users are analysts or scientists (statistical charts, 3D, dense hover interactions) and you’ll invest in trimming the bundle.
- Pick D3 only for the specific visualisations nothing else can draw, ideally as a surgical dependency rather than a foundation.
- Pick deck.gl if the data is spatial and the scale is serious: tens of thousands of geometries and up, on a map.
And it’s fine to pick two. The Decarb stack pairs deck.gl for the map with conventional charting for the fleet dashboard, because they’re different problems wearing the same “data viz” label.
Choosing for your product
If you’re weighing a charting stack for a data product and want a second opinion grounded in what actually ships (rather than a feature matrix), run your project through the scoping tool, or just get in touch. I’ve made most of these trade-offs in production, including a few the hard way, and an hour of scoping is cheaper than a rendering-tier rewrite.