Asterdex.com — The World's Leading Assets Platform. Mint Aster tokens & stake for rewards. Decentralized and secure crypto trading. Go to Aster DEX
Verifiable correctness for each action you take. Every operation is cryptographically proven to be fair and correct. Auditable by design, trustless by default. Go to Lighter

react-chartjs-2 The Only React Chart.js Tutorial You Need






react-chartjs-2: The Complete React Chart.js Guide (2025)





Complete Guide · 2025

react-chartjs-2
The Only React Chart.js Tutorial You Need

From zero to a fully interactive data visualization dashboard —
installation, customization, plugins and real-world examples,
all in one place.

Updated July 2025
⏱ 18 min read
📦 react-chartjs-2 v5 · Chart.js v4
⚡ Beginner → Advanced

📊 Semantic Keyword Clusters

🎯 Core
  • react-chartjs-2
  • React Chart.js
  • React chart component
  • Chart.js React wrapper
  • React chart library
⚙️ Setup
  • react-chartjs-2 installation
  • react-chartjs-2 setup
  • react-chartjs-2 getting started
  • npm install chart.js
  • peer dependency Chart.js v4
🖥️ Usage
  • react-chartjs-2 example
  • react-chartjs-2 tutorial
  • React Chart.js dashboard
  • bar chart React
  • line chart React component
🔬 Advanced
  • react-chartjs-2 customization
  • react-chartjs-2 plugins
  • React data visualization
  • React interactive charts
  • chartjs options object
💡 LSI / Related
  • canvas-based charts React
  • responsive charts React
  • Chart.js v4 migration
  • doughnut chart React
  • tree-shakeable Chart.js

What Is react-chartjs-2 and Why Should You Care?

If you’ve spent more than fifteen minutes Googling React chart libraries,
you’ve probably encountered a half-dozen contenders — Recharts, Nivo, Victory, ApexCharts,
ECharts. They’re all solid. But
react-chartjs-2
occupies a peculiar sweet spot: it gives you the battle-tested rendering engine of
Chart.js — the most
popular canvas-based charting library on the planet — wrapped in idiomatic React components
that feel like they were born there.

The premise is straightforward. Chart.js is a vanilla JavaScript library
that draws charts onto an HTML5 <canvas> element. It’s powerful, heavily
documented, and has an enormous plugin ecosystem. The catch: it operates through imperative
DOM mutations — not exactly React’s idea of a good time. Enter
react-chartjs-2, a thin but thoughtful wrapper that translates Chart.js’s
imperative API into declarative React components. You pass data and
options as props, React handles the lifecycle, and Chart.js does all the
pixel-pushing on canvas. Everyone wins.

As of 2025, react-chartjs-2 v5 pairs with Chart.js v4 and
ships with full TypeScript definitions, tree-shaking support, and a ref-based API for
accessing the underlying chart instance when you need to go low-level. Weekly downloads
consistently sit north of 2 million on npm. If « popular, well-maintained, and
reasonably easy to customise » sounds like a chart library you’d use, read on.

react-chartjs-2 Installation: Getting the Right Versions

The single most common source of confusion with
react-chartjs-2 installation
is peer dependencies. The library does not bundle Chart.js — you install both
separately, and the major versions must align. For any new project in 2025, the correct
incantation is:

bash

# npm
npm install react-chartjs-2 chart.js

# yarn
yarn add react-chartjs-2 chart.js

# pnpm
pnpm add react-chartjs-2 chart.js

This installs react-chartjs-2 v5.x and Chart.js v4.x
together. Both end up in your node_modules; react-chartjs-2 lists chart.js as a
peerDependency, which means it expects you to supply it. Miss this step and
you’ll get a cryptic « Cannot find module ‘chart.js' » error that has haunted many a Stack
Overflow thread at 2 AM.

⚠️ Version Pinning Warning: If you’re maintaining a legacy project on
Chart.js v2 or v3, do not blindly upgrade. Pin react-chartjs-2 to v2 for Chart.js
v2 compatibility, or v4 for Chart.js v3. Running npm ls chart.js to verify
what’s actually resolved in your tree is always a good idea before debugging phantom
rendering issues.

Once installed, you’ll notice that react-chartjs-2 v5 no longer auto-registers all Chart.js
components globally. This is intentional — it enables tree-shaking, so your production
bundle only includes the chart types you actually use. The trade-off is that you must
explicitly register the components you need via Chart.js’s ChartJS.register()
call before rendering any chart component.

react-chartjs-2 Setup: Registration and Your First Component

Tree-shaking is great for bundle size. Forgetting to register components is great for
producing completely blank charts with zero helpful error messages. Let’s avoid that.
The react-chartjs-2 setup pattern looks like this — register once,
ideally at your app’s entry point or in a dedicated chartSetup.ts file:

tsx — chartSetup.ts

import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  LineElement,
  PointElement,
  ArcElement,
  Title,
  Tooltip,
  Legend,
  Filler,
} from 'chart.js';

// Register everything your app uses — once, globally
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  LineElement,
  PointElement,
  ArcElement,
  Title,
  Tooltip,
  Legend,
  Filler
);

Import this file at the top of your main.tsx (or _app.tsx in
Next.js) before any chart components are loaded. With registration sorted, rendering your
first React chart component
is almost disappointingly straightforward:

tsx — SalesBarChart.tsx

import type { ChartData, ChartOptions } from 'chart.js';
import { Bar } from 'react-chartjs-2';
import '../chartSetup';

const data: ChartData<'bar'> = {
  labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'],
  datasets: [{
    label: 'Monthly Revenue ($)',
    data: [18500, 22300, 19800, 27400, 31200, 28900],
    backgroundColor: 'rgba(79, 142, 247, 0.75)',
    borderColor:       'rgba(79, 142, 247, 1)',
    borderWidth:       2,
    borderRadius:     6,
  }],
};

const options: ChartOptions<'bar'> = {
  responsive:         true,
  maintainAspectRatio: false,
  plugins: {
    legend: { position: 'top' as const },
    title: {
      display: true,
      text:    'H1 2025 Revenue',
    },
  },
};

export default function SalesBarChart() {
  return (
    <div style={{ height: '380px' }}>
      <Bar data={data} options={options} />
    </div>
  );
}

Note the wrapper <div> with an explicit height. Chart.js respects the
canvas’s parent container dimensions, but only when maintainAspectRatio: false
is set. Without it, Chart.js applies a default 2:1 width-to-height ratio and ignores your
container height entirely — a behaviour that confuses nearly everyone the first time.

💡 Next.js Note: If you’re using react-chartjs-2 with Next.js App Router,
add 'use client' at the top of any component file that imports chart components.
Chart.js targets the browser’s canvas API and cannot run during server-side rendering.

Chart Types: What react-chartjs-2 Puts on the Menu

The library exposes a named React component for every chart type Chart.js supports, plus
a generic <Chart /> component for when you want to specify the type
dynamically at runtime. The full roster as of Chart.js v4 includes:

Each typed component is simply a convenience wrapper that pre-sets the type
prop on the underlying <Chart /> component and narrows the TypeScript
generics accordingly. The practical upside: TypeScript will warn you if you pass a
borderRadius option to a Line chart, where it doesn’t apply. That kind of
static safety is worth a lot when you’re debugging a dashboard with twelve chart components
and a deadline tomorrow.

The mixed chart type deserves a special mention. Using the generic
<Chart /> component, you can combine, say, a bar dataset and a line
dataset in the same canvas — useful for plotting revenue bars alongside a rolling-average
line. This is done by setting a type property on individual dataset objects,
overriding the chart-level type. It’s one of those features where Chart.js’s maturity
really shows.

Advanced Data Visualization: Customization and the Options API

The options prop is where
advanced react-chartjs-2 customization
lives. Chart.js v4’s options object is deeply nested and remarkably expressive — you can
control fonts, colors, grid lines, tick formatting, animations, tooltip behavior, and axis
scales with surgical precision. Knowing where to find things in the options tree is more
than half the battle.

The most impactful axes configuration involves understanding the difference between
scales.x and scales.y definitions. For time-series charts, you’ll
want to switch the x-axis type to 'time' and install the optional
date adapter package — typically chartjs-adapter-date-fns or
chartjs-adapter-luxon. This unlocks intelligent time unit stepping, locale-aware
formatting, and proper handling of sparse data. For a production
React Chart.js dashboard,
time-axis charts are almost always the right call for anything time-stamped.

tsx — Advanced Options

const options: ChartOptions<'line'> = {
  responsive:         true,
  maintainAspectRatio: false,
  interaction: {
    mode:        'index',   // all datasets at hovered x-position
    intersect:  false,
  },
  plugins: {
    tooltip: {
      callbacks: {
        label: (ctx) =>
          `${ctx.dataset.label}: $${ctx.parsed.y.toLocaleString()}`,
      },
    },
    legend: {
      labels: {
        usePointStyle: true,
        font: { size: 13 },
      },
    },
  },
  scales: {
    x: {
      grid: { display: false },
      ticks: { color: '#8892b0' },
    },
    y: {
      grid: { color: 'rgba(255,255,255,0.06)' },
      ticks: {
        color:    '#8892b0',
        callback: (val) => `$${(+val).toLocaleString()}`,
      },
      beginAtZero: true,
    },
  },
  animation: {
    duration: 600,
    easing:   'easeInOutQuart',
  },
};

Tooltip callbacks are frequently the first place developers realise just how much
Chart.js’s options API lets you do. The label callback in the example above
formats currency values inline — no post-processing required. Similarly, the
interaction.mode: 'index' setting groups all dataset tooltips under the
cursor’s x-position, which is standard UX for any multi-series chart showing comparative
data. These aren’t obscure hacks; they’re the library working exactly as designed.

react-chartjs-2 Plugins: Extending Beyond the Defaults

Chart.js has a formal
plugin system
that react-chartjs-2 exposes cleanly through the plugins prop on any chart
component. Plugins can hook into virtually every stage of the draw cycle — before and after
datasets are drawn, before and after the legend, before and after tooltips. This makes them
genuinely powerful for custom overlays without forking the library.

A practical real-world example: annotating a chart with a horizontal « target » line.
Rather than maintaining a phantom dataset just for the line, you can use the popular
chartjs-plugin-annotation package. Install it, register it once alongside
your other Chart.js components, and pass annotation definitions through the options object.
The result is a declarative annotation system that updates reactively when your data
changes — exactly the React experience you’d hope for.

tsx — Inline Custom Plugin

import type { Plugin } from 'chart.js';

/**
 * Draws a centered text label inside a Doughnut chart.
 * Inline plugin — scoped to this component only.
 */
const centerTextPlugin: Plugin<'doughnut'> = {
  id: 'centerText',
  afterDraw(chart) {
    const { ctx, chartArea: { top, bottom, left, right } } = chart;
    const cx = (left + right)  / 2;
    const cy = (top  + bottom) / 2;

    ctx.save();
    ctx.font         = 'bold 32px Inter, sans-serif';
    ctx.fillStyle    = '#e2e8f0';
    ctx.textAlign    = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText('84%', cx, cy);
    ctx.restore();
  },
};

// Pass as the `plugins` array prop — not registered globally
<Doughnut
  data={data}
  options={options}
  plugins={[centerTextPlugin]}
/>

The key distinction to internalise: plugins registered via ChartJS.register()
are global and apply to every chart instance. Plugins passed via the
plugins prop are local and scoped to that specific component.
For plugins like the center-text example above — highly specific to one chart — local scope
is exactly right. For cross-cutting concerns like custom tooltip styling or watermarks,
global registration keeps your code DRY.

Building React Interactive Charts with Refs and Event Handlers

One of react-chartjs-2’s more powerful features is its ref forwarding. Every
chart component accepts a React ref that, when attached, gives you direct access to the
underlying Chart.js instance. This opens the door to programmatic control that goes well
beyond what props alone can express — triggering animations, reading hovered elements,
updating datasets imperatively, or calling chart.toBase64Image() to export
a PNG snapshot.

tsx — Ref-Based Instance Access

import { useRef } from 'react';
import type { ChartJS } from 'chart.js';
import { Line } from 'react-chartjs-2';

export default function InteractiveLine() {
  const chartRef = useRef<ChartJS<'line'>>(null);

  const handleExport = () => {
    if (!chartRef.current) return;
    const url = chartRef.current.toBase64Image();
    const link = document.createElement('a');
    link.href     = url;
    link.download = 'chart-export.png';
    link.click();
  };

  const handleToggleDataset = (index: number) => {
    const chart = chartRef.current;
    if (!chart) return;
    const meta = chart.getDatasetMeta(index);
    meta.hidden = !meta.hidden;
    chart.update();
  };

  return (
    <div>
      <div style={{ height: '360px' }}>
        <Line ref={chartRef} data={data} options={options} />
      </div>
      <button onClick={handleExport}>Export PNG</button>
      <button onClick={() => handleToggleDataset(0)}>Toggle Dataset</button>
    </div>
  );
}

For click-to-drill-down interactions, react-chartjs-2 provides an
onClick prop that receives the native mouse event alongside the array of
chart elements under the cursor. You can call
chart.getElementsAtEventForMode(event, 'nearest', ...) to identify exactly
which data point was clicked — essential for building dashboard charts that navigate to
detail views or filter other charts on the same page.

Real-time data streaming is another common use case. Rather than unmounting and remounting
the chart component on each data update, you should update the data prop
reference. react-chartjs-2 detects the change and calls chart.update()
internally, which triggers a smooth re-render animation. For very high-frequency updates
(say, > 10 Hz), you’ll want to pass animation: false in options and consider
using the chart ref to batch-push data imperatively to avoid React render overhead entirely.

Building a React Chart.js Dashboard: Architecture Patterns

A React data visualization dashboard is where all the concepts above come
together under real-world constraints: shared state, dynamic filters, responsive layout,
and consistent theming across multiple chart instances. The architecture decisions you make
at this stage have an outsized impact on maintainability.

The single most valuable pattern for multi-chart dashboards is
centralising your Chart.js defaults. Chart.js v4 exposes
ChartJS.defaults — a mutable object you can patch once at startup to set
global font families, colors, animation durations, and tooltip styles. Every chart instance
inherits from this baseline and only overrides what it needs to. This means your
twenty-chart dashboard doesn’t have twenty copies of font: { family: 'Inter' }
scattered across options objects — it has one, and they all just work.

tsx — Global Defaults Setup

import { Chart as ChartJS } from 'chart.js';

// Apply once — all chart instances inherit these
ChartJS.defaults.font.family        = 'Inter, system-ui, sans-serif';
ChartJS.defaults.font.size          = 13;
ChartJS.defaults.color               = '#8892b0';
ChartJS.defaults.borderColor          = 'rgba(255,255,255,0.07)';
ChartJS.defaults.animation.duration  = 500;
ChartJS.defaults.plugins.tooltip.backgroundColor = '#1a1d27';
ChartJS.defaults.plugins.tooltip.borderColor      = '#2e3350';
ChartJS.defaults.plugins.tooltip.borderWidth      = 1;

For dataset color management across many charts, define a shared
color palette array and map indices to colors consistently. This gives
the user a predictable visual language — the same category is always the same color,
regardless of which chart is showing it. Pairing this with a React context for the active
filters means every chart in the dashboard stays synchronized without prop-drilling
through five component layers.

How Does react-chartjs-2 Stack Up Against Other React Chart Libraries?

Choosing a React chart library is less about which one is « best » in the abstract and more
about which one’s trade-offs match your project’s needs. The honest answer is that
react-chartjs-2, Recharts, and Nivo each have genuinely different strengths. Here’s a
frank look at the landscape:

Library Rendering Bundle (gz) TypeScript Custom Plugins SSR Safe Best For
react-chartjs-2 Canvas ~40 KB Full Yes Client only Dashboards, high-data-density charts
Recharts SVG ~120 KB Full Via components Yes Composable, custom SVG charts
Nivo SVG / Canvas ~200 KB+ Full Limited Yes Beautiful defaults, design-heavy projects
Victory SVG ~100 KB Full Via components Yes React Native + web, simple charts
ECharts (echarts-for-react) Canvas / SVG ~800 KB Partial Yes Client only Enterprise dashboards, geo maps

Canvas rendering gives react-chartjs-2 a meaningful performance advantage with large
datasets — rendering thousands of data points on canvas is noticeably faster than
generating equivalent SVG DOM nodes. The flip side: canvas charts are harder to make
accessible and can’t be styled with CSS. If your project has strong accessibility
requirements, augmenting react-chartjs-2 charts with ARIA labels and a data table fallback
is a worth the investment.

The bundle size argument is frequently overstated. Yes, react-chartjs-2 plus Chart.js adds
~40 KB gzipped to your initial bundle — but Chart.js v4’s tree-shaking means you only pay
for what you import. A project using only bar and line charts will ship significantly less
than one importing all chart types. Compare this to Nivo, where importing a single chart
type still drags in much of the library’s shared infrastructure.

Common Pitfalls and How to Fix Them

Even experienced React developers run into a handful of sharp edges with
react-chartjs-2. The « canvas is already in use » error is the most
infamous — it happens when React’s strict mode double-invokes effects and the chart instance
isn’t properly destroyed before the second mount. The fix is either to suppress strict mode
for chart components (not ideal), or to handle cleanup via the chart ref’s
destroy() method in a useEffect return function. The library
handles this automatically in most cases, but custom canvas management can reintroduce it.

Performance regressions often trace back to unstable object references
in props. If you construct your data or options objects inline
in the render function, React creates a new object reference on every render, and
react-chartjs-2 triggers a full chart re-render even when the underlying values haven’t
changed. The solution is useMemo — wrap both data and
options with it, declaring the actual data variables as dependencies:

tsx — Stable References with useMemo

import { useMemo } from 'react';

function RevenueChart({ salesData }: { salesData: number[] }) {
  const data = useMemo(() => ({
    labels: ['Q1', 'Q2', 'Q3', 'Q4'],
    datasets: [{
      label: 'Revenue',
      data: salesData,
      backgroundColor: 'rgba(79,142,247,0.7)',
    }],
  }), [salesData]); // ← only re-computes when salesData changes

  const options = useMemo(() => ({
    responsive:         true,
    maintainAspectRatio: false,
  }), []); // ← never changes; empty dep array is correct here

  return (
    <div style={{ height: '320px' }}>
      <Bar data={data} options={options} />
    </div>
  );
}

A third pitfall catches people coming from Chart.js without the React wrapper:
direct mutation of the data object. Chart.js’s vanilla API lets you
mutate chart.data.datasets[0].data.push(newValue) and call
chart.update(). In react-chartjs-2, you should update the data
prop instead — passing a new array reference. Direct mutation bypasses React’s
reconciliation cycle and can produce state drift where what React thinks is in the chart
diverges from what’s actually drawn on canvas. Keeping the data flow unidirectional is
not just good React hygiene here; it’s the only way the library can reliably compute diffs.

✅ Pro Tip: Enable React.StrictMode during development
specifically to surface double-invocation issues early. Strict mode deliberately renders
components twice in development to expose side effects — if your charts survive strict
mode, they’re substantially less likely to misbehave in production.

The Verdict: When to Reach for react-chartjs-2

react-chartjs-2
earns its place as the default choice for React data visualization when you need a
production-quality React chart library that won’t surprise you six months
in. The combination of Chart.js’s rendering maturity, the wrapper’s clean React
integration, full TypeScript support, and a plugin ecosystem that stretches from
annotation overlays to real-time streaming means the library genuinely scales from a
single chart on a marketing page to a 30-panel analytics dashboard.

The cases where you might look elsewhere are narrow but real. If you need charts to render
server-side (for og:image generation, for instance), you’ll need a workaround — Chart.js
is canvas-first, and canvas doesn’t exist on the server. If your charts need to be
infinitely composable SVG with fine-grained CSS styling, Recharts’ component model is a
better fit. And if your dataset runs into hundreds of thousands of points that need smooth
panning and zooming, you’d want to evaluate WebGL-based alternatives like
uPlot or SciChart.

But for the overwhelming majority of real-world dashboards — the ones that show sales
trends, user analytics, system metrics, or financial summaries — react-chartjs-2 is the
pragmatic choice. It’s well-documented, actively maintained, has a massive community, and
the learning curve is genuinely shallow once you understand the registration model.
Start with a Bar chart, understand the options tree, write one custom plugin,
and you’ll have more than enough to build something genuinely impressive.


Frequently Asked Questions

Q

What is react-chartjs-2 and how is it different from Chart.js?

react-chartjs-2 is a React wrapper around the Chart.js library.
Chart.js operates directly on HTML5 canvas elements via vanilla JavaScript, while
react-chartjs-2 exposes Chart.js as idiomatic React components — letting you pass
data, options, and plugins as props and manage
chart lifecycle through React’s own rendering model. In short: same Chart.js rendering
engine, React developer experience on top.

Q

Does react-chartjs-2 support Chart.js version 4?

Yes. react-chartjs-2 v5+ is built specifically for Chart.js v4 and
requires it as a peer dependency. If you’re on a legacy project still using Chart.js
v2 or v3, pin react-chartjs-2 to the corresponding major version (v2 or v4
respectively). Always run npm ls chart.js to confirm what version is
actually resolved in your dependency tree before debugging rendering issues.

Q

How do I make react-chartjs-2 charts responsive and resize automatically?

Wrap your chart component in a container <div> with an explicit height
(e.g., style={{ height: '380px' }}) and set
responsive: true and maintainAspectRatio: false in your
options prop. Chart.js will then observe the container’s dimensions via a
ResizeObserver and automatically redraws the canvas whenever the container
size changes — no window.resize listeners or manual calls to
chart.resize() required.

Still have questions? Check the official
react-chartjs-2 documentation
and the
Chart.js v4 reference.