Updating, styling and interaction

Mutate the data then call update, control every visual with dataset and option properties, and keep the chart usable on small screens.

Updating data

// mutate the data, then re-render
chart.data.labels.push('May');
chart.data.datasets.forEach((ds) => ds.data.push(random()));
chart.update();

// update with no animation - correct for streaming or live dashboards
chart.data.datasets[0].data[0] = 42;
chart.update('none');

// drop the oldest point and keep a rolling window
chart.data.labels.shift();
chart.data.datasets.forEach((ds) => ds.data.shift());

// replace the whole dataset, then re-render
chart.data.datasets = buildDatasets(response);
chart.update();

// restore the original animation state and re-run entry animations
chart.reset();

// release the canvas and its listeners when the widget is removed
chart.destroy();
CallWhat it does
update()Recomputes scales and redraws with animation
update('none')Same, but skips animation — the choice for live data
update(mode)Accepts any animation mode name, for example 'active'
reset()Returns the chart to its original data and state
resize(w, h)Forces a size; pass nothing to re-measure the parent
destroy()Detaches observers and clears the canvas
toBase64Image()Returns a PNG data URL of the current frame
  • Chart.js 2 had addData and removeData; version 3 removed them. Mutate data and call update().
  • Batching matters: push every point you have, then call update() once instead of per point.
  • destroy() in the framework unmount path prevents listeners and animation frames leaking between route changes.

Styling options

PropertyApplies toNotes
backgroundColorAllString or array; bars and arcs fill with it
borderColor / borderWidthAllAlso drives line stroke width
borderRadiusbarRounds bar corners; a number or an object per corner
tensionline0 is straight, around 0.4 is the usual curve
filllinetrue, 'origin' or { target: '1' } to fill between datasets
pointRadius / pointHoverRadiusline, scatterSet to 0 to hide markers on dense lines
spanGapslineConnects across null values instead of breaking
cutoutdoughnutPercentage of the radius left hollow
animation.durationAllMilliseconds; 0 disables animation entirely
options: {
  animation: { duration: 400, easing: 'easeOutQuart' },
  plugins: {
    legend: {
      position: 'bottom',
      labels: { usePointStyle: true, boxWidth: 8, padding: 16 }
    },
    tooltip: {
      callbacks: {
        label: (ctx) => ctx.dataset.label + ': ' + ctx.parsed.y + ' units',
        title: (items) => 'Week of ' + items[0].label
      }
    }
  },
  scales: {
    x: { grid: { display: false } },
    y: { border: { dash: [4, 4] } }
  }
}

Responsive layout and interaction

options: {
  responsive: true,            // default: re-render when the container resizes
  maintainAspectRatio: false,  // fill the container height instead of a fixed ratio
  onResize: (chart, size) => {
    chart.options.plugins.legend.display = size.width > 480;
  },
  interaction: { mode: 'index', intersect: false },
  onClick: (event, elements, chart) => {
    if (elements.length) drillDown(elements[0].index);
  },
  plugins: {
    decimation: { enabled: true, algorithm: 'lttb', samples: 500 }
  }
}
  • interaction.mode: 'index' with intersect: false shows one tooltip per x position — far friendlier than hunting individual points.
  • The decimation plugin replaces thousands of points with a visually faithful subset; it requires parsing: false and a linear x scale.
  • For React and Vue, create the chart after the canvas is in the DOM and destroy it on unmount; reusing one instance across renders causes double-registered listeners.
⚠️
Never re-create the chart on every data change. Constructing a second Chart on the same canvas leaves the first instance observing the same element, which shows up as flicker, duplicated tooltips and steadily rising memory.

FAQ

How do I make a custom HTML tooltip?
Disable the built-in one and use plugins.tooltip.external: keep a reference to a positioned element, read tooltipModel.body and place it near tooltipModel.caretX/Y on each call.
Why is my line chart jagged or wrong after an update?
Usually the labels and the data arrays drifted out of sync — one got pushed without the other. Keep labels and every dataset the same length, or switch to { x, y } points so the data carries its own axis values.

Chart types and datasets Interaction, resize and data updates

Last refreshed 2026-09-18.