# How to Update ECharts Data Dynamically Without Re-Rendering the Entire Chart

> Dynamically update ECharts data efficiently. Use appendData to add points without re-rendering the entire chart, saving performance and maintaining canvas state.

- Repository: [The Apache Software Foundation/echarts](https://github.com/apache/echarts)
- Tags: performance
- Published: 2026-03-04

---

**Use the `appendData` API to push new points into a specific series while preserving the existing canvas state, avoiding the costly full re-render triggered by `setOption`.**

When building real-time dashboards or streaming data visualizations with **apache/echarts**, calling `setOption` for every new data point causes the entire chart to rebuild. This guide explains how to update ECharts data dynamically without re-rendering the whole chart by leveraging the internal `appendData` pipeline.

## Why `setOption` Triggers a Full Re-Render

When you invoke `chart.setOption(option)`, ECharts performs a deep merge of the entire option tree in [`src/core/echarts.ts`](https://github.com/apache/echarts/blob/main/src/core/echarts.ts) (lines 1539–1564). The **Scheduler** ([`src/core/Scheduler.ts`](https://github.com/apache/echarts/blob/main/src/core/Scheduler.ts), line 214) then schedules a complete view refresh, walking through every series, component, and graphic element. For large datasets or high-frequency updates, this full pipeline creates significant CPU and memory overhead because the canvas (or SVG) is cleared and redrawn completely.

## The `appendData` API: Incremental Updates Without Re-Rendering

The `appendData` method provides a dedicated pathway for streaming data into an existing series without disturbing the rest of the chart. According to the apache/echarts source code, this API bypasses the heavy option-merge logic and writes directly to the underlying data storage.

### How `appendData` Works Internally

The call chain demonstrates the efficiency of this approach:

1. **Entry Point** – [`src/core/echarts.ts`](https://github.com/apache/echarts/blob/main/src/core/echarts.ts) receives `chart.appendData({ seriesIndex: 0, data: [...] })` and routes it to the target `SeriesModel`.
2. **Series Delegation** – In [`src/model/Series.ts`](https://github.com/apache/echarts/blob/main/src/model/Series.ts) (lines 334–339), `SeriesModel.appendData` forwards the payload to its `SeriesData` instance.
3. **Data Store Mutation** – [`src/data/SeriesData.ts`](https://github.com/apache/echarts/blob/main/src/data/SeriesData.ts) (lines 571–574) passes the raw array to `DataStore.appendData` in [`src/data/DataStore.ts`](https://github.com/apache/echarts/blob/main/src/data/DataStore.ts) (lines 318–322). Here, the new items are pushed into the underlying raw array; no new series objects are created, and existing graphic elements remain attached to their old indices.
4. **Partial View Update** – The scheduler flags the series for an incremental update. [`src/view/Chart.ts`](https://github.com/apache/echarts/blob/main/src/view/Chart.ts) (lines 284–304) executes `incrementalPrepareRender` and `incrementalRender`, painting only the new marks onto the existing canvas.

### The Incremental Render Pipeline

Because the view is updated incrementally, the canvas is not cleared. The **Scheduler** ([`src/core/Scheduler.ts`](https://github.com/apache/echarts/blob/main/src/core/Scheduler.ts)) detects the `appendData` flag and skips the full `render` cycle, invoking only the incremental methods for the affected series. This yields smooth, low-latency updates even when streaming millions of points.

## Practical Code Examples

### Setting Up a Chart for Streaming Data

Initialize a line chart with an empty dataset to prepare for dynamic updates:

```javascript
var chart = echarts.init(document.getElementById('main'));

chart.setOption({
    xAxis: { type: 'category' },
    yAxis: { type: 'value' },
    series: [{
        type: 'line',
        data: []
    }]
});

```

### Appending Data to a Specific Series

Push new points every second without re-rendering the entire chart:

```javascript
setInterval(function () {
    var newPoints = [];
    for (var i = 0; i < 3; i++) {
        newPoints.push(Math.round(Math.random() * 100));
    }
    
    // seriesIndex: 0 targets the first series
    chart.appendData({
        seriesIndex: 0,
        data: newPoints
    });
}, 1000);

```

### Using Series IDs Instead of Indices

For maintainability, reference series by the `id` defined in `setOption`:

```javascript
chart.appendData({
    seriesId: 'realTimeSeries',
    data: [10, 20, 30]
});

```

### Replacing Full Dataset Without Structural Rebuild

If you must replace all data but want to avoid option-merge overhead, use `notMerge: true`. Note this still triggers a full repaint, unlike `appendData`:

```javascript
chart.setOption({
    series: [{ data: bigNewArray }]
}, { notMerge: true });

```

## Performance Comparison: `appendData` vs `setOption`

| Method | Re-render Scope | Use Case | Internal Mechanism |
|--------|----------------|----------|-------------------|
| **`appendData`** | Affected series only | Streaming, real-time feeds | Direct `DataStore` mutation; `incrementalRender` |
| **`setOption`** | Entire chart | Structural changes, initial load | Deep option merge; full `render` pipeline |
| **`setOption` with `notMerge: true`** | Entire chart (full repaint) | Dataset replacement, schema changes | Model rebuild; full repaint |

Reserve `setOption` for changes to axes, legends, or series configuration. For high-frequency data updates, `appendData` is the only method that preserves the existing render state and maintains 60fps performance.

## Summary

- Use **`chart.appendData`** to update ECharts data dynamically without re-rendering the entire chart, preserving canvas state and achieving high-performance streaming.
- The method writes directly to [`src/data/DataStore.ts`](https://github.com/apache/echarts/blob/main/src/data/DataStore.ts), bypassing the expensive option-merge logic in [`src/core/echarts.ts`](https://github.com/apache/echarts/blob/main/src/core/echarts.ts).
- Incremental rendering in [`src/view/Chart.ts`](https://github.com/apache/echarts/blob/main/src/view/Chart.ts) paints only new marks, leaving existing elements untouched.
- Use **`setOption`** only for structural changes or initial configuration, and **`setOption` with `notMerge: true`** for complete dataset replacements that require a full repaint.

## Frequently Asked Questions

### What is the difference between `appendData` and `setOption` in ECharts?

`appendData` is a specialized API for streaming new data points into an existing series without triggering a full chart re-render. It mutates the underlying `DataStore` directly and invokes an incremental render. `setOption` performs a deep merge of the entire configuration tree and schedules a complete repaint of every series and component, making it suitable for structural changes but expensive for frequent updates.

### Can I use `appendData` with multiple series at once?

No, `appendData` targets a single series per call. You must specify either `seriesIndex` (zero-based position) or `seriesId` (the string identifier set in the series configuration). To update multiple series simultaneously, invoke `appendData` separately for each series; the scheduler will batch the incremental renders efficiently.

### Does `appendData` work with all chart types?

`appendData` works best with series that support incremental rendering, such as `line`, `scatter`, and `custom` series. It is designed for Cartesian coordinate systems where data points are appended to the end of the dataset. Chart types requiring complete data recomputation (such as pie charts or treemaps) may not benefit from incremental updates and should use `setOption` instead.

### How do I clear old data when using `appendData`?

`appendData` only adds data; it does not remove existing points. To clear historical data while keeping the chart instance alive, you must call `setOption` with a new empty data array and `notMerge: true` to reset the series, then resume appending. Alternatively, maintain a sliding window in your application logic and use `setOption` with `replaceMerge: ['series']` to update the dataset efficiently without a full structural rebuild.