Chart types and datasets
The eight built-in types, how datasets carry their own styling, mixed charts with two scales, and how data gets parsed.
The built-in types
type | Shape of data | Typical use |
|---|---|---|
bar | One value per label | Category comparison; indexAxis: 'y' flips it to horizontal |
line | One value per label or {x, y} | Trends; fill and tension control the look |
pie | One value per label | Single part-to-whole snapshot |
doughnut | One value per label | Same, with a cutout percentage hole |
polarArea | One value per label | Values as sector radius — an unusual but readable alternative |
radar | One value per axis label | Comparing several metrics for a few entities |
scatter | { x, y } objects | Correlation between two measures |
bubble | { x, y, r } objects | Adding a third dimension through radius |
// a different default colour per point is a dataset property, not a global one
new Chart(canvas, {
type: 'doughnut',
data: {
labels: ['Direct', 'Search', 'Social'],
datasets: [{
data: [55, 30, 15],
backgroundColor: ['#4f46e5', '#0ea5e9', '#f59e0b'],
borderWidth: 2,
borderColor: '#fff',
hoverOffset: 8
}]
},
options: { cutout: '62%', plugins: { legend: { position: 'right' } } }
});Datasets and mixed charts
new Chart(canvas, {
type: 'bar', // the base type
data: {
labels: ['Jan', 'Feb', 'Mar', 'Apr'],
datasets: [
{ label: 'Units', data: [12, 19, 8, 15], backgroundColor: '#4f46e5' },
{ label: 'Returns', data: [1, 2, 1, 3], backgroundColor: '#f97316', stack: 'flow' },
{
type: 'line', // override the type per dataset
label: 'Trend',
data: [9, 14, 12, 16],
yAxisID: 'y1', // pin to the second scale
tension: 0.3,
pointRadius: 3
}
]
},
options: {
scales: {
y: { position: 'left', beginAtZero: true },
y1: { position: 'right', beginAtZero: true, grid: { drawOnChartArea: false } }
}
}
});- Datasets in the same chart must have the same number of entries unless they use object data with explicit
xvalues. ordercontrols draw order within a chart; the line dataset above is drawn over the bars by default because it comes later.stack: 'name'groups datasets that should accumulate together.- A dataset can carry a nested
backgroundColorarray with one colour per point, which is how you highlight outliers.
Scales and parsing
| Scale option | Effect |
|---|---|
beginAtZero: true | Starts the axis at zero — the honest default for bars |
suggestedMin / suggestedMax | Hints the range without forcing it |
type: 'logarithmic' | For values spanning orders of magnitude |
type: 'time' | Real dates on the axis; needs a date adapter and its parser |
stacked: true | Accumulates datasets sharing a stack id on that axis |
grid.drawOnChartArea: false | Hides grid lines for a secondary axis |
ticks.callback | Formats tick labels, for example adding a currency prefix |
options: {
parsing: false, // data is already { x, y } - skip the conversion pass
normalized: true, // rows are sorted and unique
scales: {
x: {
type: 'time',
time: { unit: 'day' },
ticks: { maxRotation: 0, autoSkipPadding: 16 }
},
y: { ticks: { callback: (v) => v + ' k' } }
}
}💡
parsing: false is the single biggest performance win for large datasets, but it only works when the data is already in { x, y } form and sorted by x. With the wrong input the chart renders confused or empty rather than throwing.FAQ
How do I make a horizontal bar chart?
Set
options.indexAxis: 'y' on a bar chart. The labels move to the y axis automatically, and stacked bars stack horizontally.Can two datasets share one legend entry?
Not directly. The legend is generated from dataset labels, so either merge the data, hide one label with
plugins.legend.labels.filter, or build a custom HTML legend from the callback.Related
Setup and configuration Updating, styling and interaction
Last refreshed 2026-09-18.