Getting started
Installation
npm install chart.js
<!-- CDN -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
Auto-register (development)
import Chart from "chart.js/auto";
Imports all chart types and components.
Tree-shakeable (production)
import {
Chart,
LineController,
LineElement,
PointElement,
LinearScale,
CategoryScale,
Title,
} from "chart.js";
Chart.register(
LineController,
LineElement,
PointElement,
LinearScale,
CategoryScale,
Title,
);
Import only what you need.
Basic example
<canvas id="myChart"></canvas>
new Chart(document.getElementById("myChart"), {
type: "bar",
data: {
labels: ["Red", "Blue", "Yellow"],
datasets: [
{
label: "# of Votes",
data: [12, 19, 3],
borderWidth: 1,
},
],
},
options: {
scales: {
y: { beginAtZero: true },
},
},
});
Chart Types
Available types
| Type | Controller | Elements |
|---|---|---|
'line' |
LineController | LineElement, PointElement |
'bar' |
BarController | BarElement |
'pie' |
PieController | ArcElement |
'doughnut' |
DoughnutController | ArcElement |
'radar' |
RadarController | LineElement, PointElement |
'polarArea' |
PolarAreaController | ArcElement |
'bubble' |
BubbleController | PointElement |
'scatter' |
ScatterController | PointElement |
Horizontal bar
{
type: 'bar',
options: {
indexAxis: 'y'
}
}
Data Structure
Array format
{
labels: ['Jan', 'Feb', 'Mar'],
datasets: [{
label: 'Sales',
data: [65, 59, 80],
backgroundColor: 'rgba(255, 99, 132, 0.2)',
borderColor: 'rgb(255, 99, 132)',
borderWidth: 1
}]
}
Object format
datasets: [
{
data: [
{ x: 10, y: 20 },
{ x: 15, y: 25 },
],
},
];
For scatter and bubble charts.
Options
Basic options
options: {
responsive: true,
maintainAspectRatio: false,
aspectRatio: 2
}
Plugins
options: {
plugins: {
legend: {
position: 'top',
display: true
},
tooltip: {
enabled: true,
mode: 'index',
intersect: false
},
title: {
display: true,
text: 'My Chart'
}
}
}
Animations
options: {
animation: {
duration: 1000,
easing: 'easeOutQuart'
}
}
Scales
Y-axis configuration
options: {
scales: {
y: {
beginAtZero: true,
min: 0,
max: 100,
ticks: {
callback: value => `$${value}`
}
}
}
}
Stacked charts
options: {
scales: {
x: { stacked: true },
y: { stacked: true }
}
}
Tooltips
Custom callbacks
plugins: {
tooltip: {
callbacks: {
label: (ctx) => {
const val = ctx.parsed.y;
return `${ctx.dataset.label}: $${val}`;
};
}
}
}
External HTML tooltip
{
enabled: false,
external: function(context) {
// Custom implementation
}
}
Legend
Position and alignment
plugins: {
legend: {
position: 'top', // 'left', 'bottom', 'right'
align: 'center' // 'start', 'end'
}
}
Custom click handler
plugins: {
legend: {
onClick: (e, item, legend) => {
const i = item.datasetIndex;
legend.chart.isDatasetVisible(i)
? legend.chart.hide(i)
: legend.chart.show(i);
};
}
}
Animations
Animation callbacks
options: {
animation: {
duration: 1000,
easing: 'easeOutQuart',
onComplete: (anim) => {
console.log('Done');
},
onProgress: (anim) => {
console.log(anim.currentStep);
}
}
}
Disable specific animations
options: {
animations: {
colors: false,
x: false
}
}
Disable all: animation: false
Plugins
Creating a plugin
const bgPlugin = {
id: "customBg",
beforeDraw: (chart, args, options) => {
const { ctx } = chart;
ctx.save();
ctx.fillStyle = options.color || "#fff";
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
},
};
new Chart(ctx, {
plugins: [bgPlugin],
options: {
plugins: {
customBg: { color: "#f5f5f5" },
},
},
});
Global registration
Chart.register(bgPlugin);
Updating Charts
Add data
chart.data.labels.push("New");
chart.data.datasets.forEach((ds) => {
ds.data.push(newVal);
});
chart.update();
Remove data
chart.data.labels.pop();
chart.data.datasets.forEach((ds) => {
ds.data.pop();
});
chart.update();
Update without animation
chart.update("none");
Destroy chart
chart.destroy();
Styling
Dataset styling
{
borderColor: 'rgb(75, 192, 192)',
borderWidth: 3,
borderDash: [5, 5],
tension: 0.1, // Bezier curve
pointStyle: 'circle', // 'cross', 'rect', 'star'
pointRadius: 3,
fill: true // Area fill
}
Color formats
"#ff6384";
"rgb(255, 99, 132)";
"rgba(255, 99, 132, 0.2)";
Responsive
HTML container
<div style="position: relative; width: 80vw; height: 40vh;">
<canvas id="chart"></canvas>
</div>
Canvas needs relative container.
Responsive options
options: {
responsive: true,
maintainAspectRatio: false,
onResize: (chart, size) => {
console.log('Resized');
}
}
React Integration
react-chartjs-2
import { Line } from "react-chartjs-2";
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
} from "chart.js";
ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement);
function MyChart() {
return <Line data={data} options={options} />;
}
Gotchas
Tree-shaking
⚠️ chart.js/auto disables tree-shaking — import and register components individually for production.
Canvas sizing
⚠️ Canvas needs a relatively-positioned container — direct canvas sizing doesn't work for responsive charts.
Pie vs Doughnut
⚠️ Pie cutout: 0 vs Doughnut cutout: '50%' — same chart type with different defaults.
Animation callbacks
⚠️ onProgress/onComplete callbacks only work at options.animation root level.
Plugin IDs
⚠️ Plugin IDs must be unique and follow npm naming conventions.
Scale references
⚠️ Scale references become invalid after updating chart.options.scales.
Canvas reuse
⚠️ Must call chart.destroy() before creating a new chart on the same canvas.
Also see
- Chart.js Docs (chartjs.org)
- Getting Started (chartjs.org)
- Configuration (chartjs.org)
- Chart Types (chartjs.org)
- Plugins (chartjs.org)
- Animations (chartjs.org)