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

typeShape of dataTypical use
barOne value per labelCategory comparison; indexAxis: 'y' flips it to horizontal
lineOne value per label or {x, y}Trends; fill and tension control the look
pieOne value per labelSingle part-to-whole snapshot
doughnutOne value per labelSame, with a cutout percentage hole
polarAreaOne value per labelValues as sector radius — an unusual but readable alternative
radarOne value per axis labelComparing several metrics for a few entities
scatter{ x, y } objectsCorrelation between two measures
bubble{ x, y, r } objectsAdding 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 x values.
  • order controls 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 backgroundColor array with one colour per point, which is how you highlight outliers.

Scales and parsing

Scale optionEffect
beginAtZero: trueStarts the axis at zero — the honest default for bars
suggestedMin / suggestedMaxHints 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: trueAccumulates datasets sharing a stack id on that axis
grid.drawOnChartArea: falseHides grid lines for a secondary axis
ticks.callbackFormats 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.

Setup and configuration Updating, styling and interaction

Last refreshed 2026-09-18.