apexcharts, chart.js, echarts, and recharts are popular JavaScript libraries used to create interactive data visualizations like line charts, bar graphs, and pie charts. recharts is built specifically for React using SVG elements, offering a declarative component-based approach. apexcharts is a modern SVG-based library that works with vanilla JavaScript and has wrappers for frameworks. chart.js is a lightweight Canvas-based library known for simplicity and responsiveness. echarts is a powerful Canvas-based library from Apache, capable of handling large datasets and complex geographic visualizations.
When building data-driven interfaces, selecting the right charting library impacts performance, maintainability, and user experience. apexcharts, chart.js, echarts, and recharts all solve the same problem but take different architectural paths. Let's compare how they handle rendering, configuration, React integration, and data updates.
The underlying rendering technology dictates how the chart scales and interacts with the DOM.
recharts uses SVG elements exclusively.
// recharts: SVG based
<LineChart width={500} height={300} data={data}>
<Line type="monotone" dataKey="uv" stroke="#8884d8" />
</LineChart>
apexcharts also uses SVG.
// apexcharts: SVG based
var options = { series: [{ data: [10, 20] }], chart: { type: 'line' } };
var chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
chart.js uses HTML5 Canvas.
// chart.js: Canvas based
const ctx = document.getElementById('myChart');
new Chart(ctx, { type: 'line', data: { datasets: [{ data: [10, 20] }] } });
echarts primarily uses Canvas (supports SVG optionally).
// echarts: Canvas based (default)
var chart = echarts.init(document.getElementById('main'));
chart.setOption({ series: [{ type: 'line', data: [10, 20] }] });
How you define the chart structure varies from JSX components to configuration objects.
recharts is Declarative.
// recharts: Declarative JSX
<BarChart data={data}>
<XAxis dataKey="name" />
<Bar dataKey="value" fill="#82ca9d" />
</BarChart>
apexcharts is Imperative Configuration.
// apexcharts: Options Object
var options = {
chart: { type: 'bar' },
series: [{ data: [10, 20] }],
xaxis: { categories: ['A', 'B'] }
};
chart.js is Imperative Configuration.
// chart.js: Config Object
new Chart(ctx, {
type: 'bar',
data: { labels: ['A', 'B'], datasets: [{ data: [10, 20] }] },
options: { responsive: true }
});
echarts is Imperative Configuration.
option object that controls every detail.// echarts: Option Object
chart.setOption({
xAxis: { type: 'category', data: ['A', 'B'] },
series: [{ type: 'bar', data: [10, 20] }]
});
Since recharts is React-native, others require wrappers or useEffect hooks to function in React.
recharts is Native React.
// recharts: Native Component
function MyChart({ data }) {
return <LineChart data={data}><Line dataKey="val" /></LineChart>;
}
apexcharts requires react-apexcharts (wrapper).
apexcharts package is vanilla; React needs a separate wrapper.// apexcharts: React Wrapper
import Chart from "react-apexcharts";
function MyChart() {
return <Chart type="line" series={[{ data: [10] }]} options={{}} />;
}
chart.js requires react-chartjs-2 (wrapper).
chart.js package does not know about React.// chart.js: React Wrapper
import { Line } from 'react-chartjs-2';
function MyChart() {
return <Line data={{ datasets: [{ data: [10] }] }} />;
}
echarts requires Manual useRef or echarts-for-react.
useEffect hook.// echarts: Manual Hook
useEffect(() => {
const chart = echarts.init(ref.current);
chart.setOption({ series: [{ data: [10] }] });
}, []);
Handling dynamic data changes differs significantly between declarative and imperative libraries.
recharts handles updates Automatically.
// recharts: Auto Update
<LineChart data={newData}>
<Line dataKey="value" />
</LineChart>
apexcharts handles updates Via Instance.
chart.updateSeries() to change data efficiently.// apexcharts: Manual Update
chart.updateSeries([{ data: [10, 20, 30] }]);
chart.js handles updates Via Instance.
chart.data array and call chart.update().// chart.js: Manual Update
chart.data.datasets[0].data = [10, 20, 30];
chart.update();
echarts handles updates Via setOption.
setOption again merges new config with old.// echarts: Merge Update
chart.setOption({ series: [{ data: [10, 20, 30] }] });
Customizing tooltips is a common requirement that reveals API flexibility.
recharts uses Custom Components.
tooltip prop.// recharts: Custom Tooltip
<Tooltip content={<CustomTooltipComponent />} />
apexcharts uses Callback Functions.
tooltip.y.val formatter function in options.// apexcharts: Formatter
tooltip: { y: { formatter: (val) => "$" + val } }
chart.js uses Callback Functions.
options.plugins.tooltip.callbacks.// chart.js: Callback
options: { plugins: { tooltip: { callbacks: { label: (ctx) => ctx.raw } } } }
echarts uses Formatter Strings or Functions.
// echarts: Formatter
tooltip: { formatter: '{b}: {c} USD' }
| Feature | recharts | apexcharts | chart.js | echarts |
|---|---|---|---|---|
| Rendering | SVG | SVG | Canvas | Canvas |
| React Support | Native | Wrapper | Wrapper | Manual/Wrapper |
| Config Style | JSX Components | Options Object | Options Object | Options Object |
| Best For | React Apps | Modern Dashboards | Simple Charts | Big Data / Maps |
| Learning Curve | Low (for React) | Low | Low | High |
recharts is the natural choice for React-heavy teams who want to compose charts like any other UI component. It reduces boilerplate but relies on SVG, which may lag with huge datasets.
apexcharts offers a balanced middle ground with beautiful defaults and SVG clarity. It is excellent for standard business dashboards where look-and-feel matters more than raw performance.
chart.js remains the lightweight champion for simple needs. If you just need a quick pie or line chart without heavy dependencies, it is reliable and easy to drop in.
echarts is the powerhouse for complex scenarios. Use it when you need geo-maps, millions of data points, or highly customized interactions that other libraries cannot handle.
Final Thought: There is no single best library â only the best fit for your stack. If you live in React, start with recharts. If you need performance at scale, look at echarts. For general-purpose simplicity, chart.js and apexcharts remain solid contenders.
Choose apexcharts if you need ready-to-use interactive charts with minimal configuration and a modern look out of the box. It is a great fit for dashboards where you want SVG quality without writing complex rendering logic. It works well in vanilla JS projects or with React via its official wrapper.
Choose chart.js if you prioritize small bundle size and need a simple, reliable solution for standard chart types. It is ideal for projects where Canvas rendering is acceptable and you do not need deep customization of internal chart elements. Its simplicity makes it easy to maintain for small to medium-sized applications.
Choose echarts if you need to visualize large datasets or require advanced features like geo-maps and complex interactions. It is suitable for enterprise-grade applications where performance with thousands of data points is critical. The learning curve is steeper, but the flexibility is unmatched for heavy-duty visualization tasks.
Choose recharts if your application is built on React and you prefer a declarative, component-driven development style. It allows you to compose charts using JSX, making it easy to integrate with your existing React state and props. It is best for applications where SVG interactivity and React ecosystem compatibility are top priorities.

Modern, interactive JavaScript charts your users will love, built for dashboards, SaaS, and data-heavy UIs.
Live demos ¡ Documentation ¡ License
@types/* install neededVersion 6 turns a chart from a picture you look at into a surface you investigate, author, and share. Most features below are opt-in and tree-shakeable; existing configs keep working unchanged.
ApexCharts.registerPlugin(def), then activate per chart with plugins: [{ name }].chart: { renderer: 'auto' } paints the series layer to canvas above a point threshold while axes, tooltips, annotations, and exports stay SVG. Hundreds of thousands of points, same config.chart: { history: { enabled: true } } records zooms, series toggles, option changes, and annotation edits. Ctrl-Z just works, and chart.history exposes undo, redo, jump, and transactions.chart.perspectives.capture() serializes the exact view (zoom window, hidden series, selections, annotations, theme) into a compact token you can put in a URL and restore anywhere.--apx-* CSS custom properties once and every chart reads them; theme: { follow: 'os' } tracks the system light/dark preference with zero JS, and ApexCharts.registerTheme registers named brand themes.ApexCharts.registerSeriesType(name, { renderItem }) draws primitives per datum and inherits tooltips, events, legend, and keyboard navigation for free.chart.animations.easing accepts named curves, cubic-bezier arrays, or functions; add your own with ApexCharts.registerEasing.ApexCharts.crossfilter. Click a slice or brush a range in one chart and every linked chart filters to match.chart: { ink: { enabled: true } } makes annotations draggable and resizable, adds click-to-create, snap, and a floating editor card (rename, recolor, restyle, delete), all wired into undo.chart.measure).chart.contextMenu).chart.streaming bounds memory for long-running feeds.chart.storyboard.bind({ beats }) pairs prose sections with saved chart views; the chart morphs to each view as the reader scrolls and reverses when they scroll back.npm install apexcharts
Or via CDN:
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
import ApexCharts from 'apexcharts'
const chart = new ApexCharts(document.querySelector('#chart'), {
chart: { type: 'bar' },
series: [{ name: 'Sales', data: [30, 40, 35, 50, 49, 60, 70, 91, 125] }],
xaxis: { categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999] }
})
chart.render()
Browse 100+ ready-to-use samples: copy, paste, ship.
Combine any of the above as mixed/combo charts, stacked variants, sparklines, or synchronized multi-chart layouts.
Official:
Community:
Render chart HTML on the server, then hydrate in the browser. Works with Next.js, Nuxt, SvelteKit, Astro, Remix, and any Node-based framework.
// Server
import ApexCharts from 'apexcharts/ssr'
const chartHTML = await ApexCharts.renderToHTML({
chart: { type: 'bar' },
series: [{ data: [30, 40, 35, 50, 49, 60, 70, 91, 125] }],
xaxis: { categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999] }
}, { width: 500, height: 300 })
// Returns hydration-ready HTML with embedded SVG
// Client
import ApexCharts from 'apexcharts/client'
ApexCharts.hydrate(document.getElementById('my-chart'))
// or: ApexCharts.hydrateAll()
No more dynamic(() => import(...), { ssr: false }) workarounds: the chart renders on the server and becomes interactive on hydration.
By default import ApexCharts from 'apexcharts' includes everything. For smaller bundles, import from apexcharts/core and add only what you need:
import ApexCharts from 'apexcharts/core' // bare class: no chart types, no features
// Chart types (match the value of chart.type)
import 'apexcharts/line'
import 'apexcharts/bar'
// import 'apexcharts/area'
// import 'apexcharts/scatter'
// import 'apexcharts/unit' // dot / pictogram / waffle / beeswarm (premium; 'waffle' aliases this)
// Optional features
import 'apexcharts/features/legend'
import 'apexcharts/features/toolbar' // zoom/pan toolbar
// import 'apexcharts/features/exports' // SVG/PNG/CSV download
// import 'apexcharts/features/annotations'
// import 'apexcharts/features/keyboard' // keyboard navigation
// import 'apexcharts/features/drilldown' // hierarchical drill-down
// import 'apexcharts/features/morph' // animated chart-type morphs
// import 'apexcharts/features/history' // undo/redo (premium)
// import 'apexcharts/features/perspectives' // shareable view state (premium)
// import 'apexcharts/features/storyboard' // scrollytelling, incl. perspectives (premium)
// import 'apexcharts/features/facet' // design tokens + OS themes
// import 'apexcharts/features/weave' // plugin platform
// import 'apexcharts/features/marks' // custom series types
// import 'apexcharts/features/link' // crossfilter / linked views (premium)
// import 'apexcharts/features/ink' // on-chart annotation editing (premium)
// import 'apexcharts/features/measure' // measure/delta ruler (premium)
// import 'apexcharts/features/context-menu' // right-click context menu (premium)
// import 'apexcharts/features/renderer-canvas' // canvas series renderer
See the tree-shaking guide for the complete list of entry points.
Most of ApexCharts is free and open source. A small set of advanced features are premium and require a license key:
| Feature | Enabled by |
|---|---|
| Unit chart type (dot / pictogram / waffle / beeswarm) | chart.type: 'unit' / chart.type: 'waffle' |
| Storyboard (scrollytelling) | chart.storyboard.bind(...) |
| Linked views / crossfilter | chart.link.enabled / chart.link.dimension / ApexCharts.crossfilter() |
| Ink layer (on-chart annotation editing) | chart.ink.enabled |
| Measure / delta ruler | chart.measure.enabled |
| Context menu (right-click) | chart.contextMenu.enabled |
| Perspectives (shareable view state) | chart.perspectives.apply() / .save() / ApexCharts.perspectives.decode() |
| History (undo/redo) | chart.history.enabled |
Without a valid key these features still work (trial mode), but the chart shows an "APEXCHARTS" watermark. A valid key removes it. Every other chart type and feature is free and never watermarked; the unit chart type (listed above, aliased by waffle) is the one premium chart type.
import ApexCharts from 'apexcharts'
// Set once, before rendering. Applies to every chart on the page.
ApexCharts.setLicense('APEX-xxxxxxxx')
Alternatives to setLicense:
// Global variable (used when setLicense was not called):
window.Apex = { license: 'APEX-xxxxxxxx' }
// Per-chart override (most specific wins):
new ApexCharts(el, { chart: { license: 'APEX-xxxxxxxx' /* ... */ } })
Precedence per chart: chart.license -> ApexCharts.setLicense() -> window.Apex.license -> unlicensed (trial). The watermark is re-evaluated on every render, so a late setLicense(validKey) followed by chart.update() clears it.
Keys are shared across the whole ApexCharts family (apexgantt, apextree, apexsankey, apex-grid-enterprise, apexstock), so one customer key works everywhere. Get a license at apexcharts.com/pricing.
ApexCharts works in all modern evergreen browsers (Chrome, Firefox, Safari, Edge). For server-side rendering, Node.js 18+ is required.
npm install
npm run dev # vite build --watch
npm test # e2e + unit
See CONTRIBUTING.md for setup, coding conventions, and PR guidelines.
ApexCharts uses a revenue-based license:
Full terms: apexcharts.com/license
We've partnered with Infragistics, creators of Ignite UI: high-performance data grids that handle unlimited rows and columns, with custom templates and real-time updates.
Available for:
Angular ¡ React ¡ Blazor ¡ Web Components ¡ jQuery