Updating Chart.js Data Dynamically Without Re-initializing the Canvas
Learn how to use the Chart.js update() method to implement real-time data streams and sliding windows without the flicker or memory leaks caused by canvas re-initialization.
25 Dec 2025, 15:59 UTC

The Problem: Avoiding Canvas Flicker and Memory Leaks
When building dashboards or real-time telemetry views, a common mistake is destroying and recreating the entire Chart.js instance every time new data arrives. This approach causes a visible flicker, resets animations, and can lead to significant memory leaks as the browser struggles to garbage collect discarded canvas contexts.
The efficient solution is to modify the existing data arrays by reference and trigger the update() method. This allows Chart.js to calculate the difference between the old and new states and animate the transition smoothly.
Prerequisites
- Chart.js v3.x or v4.x installed via npm or CDN.
- A rendered chart instance stored in a variable accessible to your data-update function.
- A data source (WebSocket, API polling, or interval timer) providing new values.
Implementing a Sliding Window Update
To create a real-time "scrolling" effect, you must synchronize the update of the labels array and the data array. If you add a point to the data but not a label to the axis, the chart will misalign or fail to render the new point.
Run the following logic within your client-side JavaScript environment. Ensure you have a reference to your chart object (e.g., myChart) and the necessary permissions to manipulate the DOM.
// Assume myChart is already initialized
function addData(chart, label, newDataPoint) {
// 1. Update the labels array
chart.data.labels.push(label);
// 2. Update the dataset values
// Accessing the first dataset [0]
chart.data.datasets[0].data.push(newDataPoint);
// 3. Maintain a sliding window (e.g., keep only 10 points)
if (chart.data.labels.length > 10) {
chart.data.labels.shift(); // Remove oldest label
chart.data.datasets[0].data.shift(); // Remove oldest data point
}
// 4. Trigger the re-render
chart.update();
}
Optimizing Performance for High-Frequency Streams
Calling update() on every single packet in a high-frequency stream (e.g., 60Hz) will block the browser's main thread, leading to UI lag. You can optimize this by controlling the animation mode.
| Update Mode | Configuration | Best Use Case |
|---|---|---|
| Default | chart.update() |
Occasional updates where smooth transitions are preferred. |
| None | chart.update('none') |
High-frequency telemetry where animation causes visual lag. |
| Resize | chart.update('resize') |
Updating data while simultaneously changing container dimensions. |
Verification and Diagnostics
To ensure the implementation is working correctly without leaking memory or blocking the thread, perform these checks:
- Visual Transition: Verify that points slide from right to left rather than the entire chart flashing white.
- Axis Sync: Check that the X-axis labels match the number of data points currently visible.
- Memory Profile: Open Browser DevTools > Memory tab. Take a heap snapshot, run the update loop for 60 seconds, and take another snapshot. The
Chartobject count should remain constant.
Rollback and State Reset
Because update() modifies the internal state of the chart, you cannot "undo" a data push without manually manipulating the arrays. To reset the chart to its original state, clear the arrays and call update:
myChart.data.labels = [];
myChart.data.datasets.forEach((dataset) => {
dataset.data = [];
});
myChart.update();
Limitations
Modifying data by reference is highly performant, but replacing the entire chart.data object (e.g., chart.data = { ...newObject }) may force Chart.js to re-initialize internal metadata, negating the performance benefits of the update() method. Always push/shift individual elements or update specific indices.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.