Update Chart Data Dynamically

Four methods change an ApexCharts chart after it has rendered:

MethodUse when
updateSeries(newSeries)Replacing all data (new API response, filter change)
appendData(newData)Pushing new points onto existing series (streaming, polling)
updateOptions(options)Changing config, or config and data together
appendSeries(newSerie)Adding a new series line or bar to an existing chart

All four return a Promise<ApexCharts> that resolves after the DOM has updated.


updateSeries

Replaces the entire series array and re-renders.

chart.updateSeries([
  { name: 'Revenue', data: [42, 51, 38, 67, 80] }
])

Pass the complete series array even when only one series changes. There is no method to update a single series by index.

When the series count stays the same and the chart is not currently zoomed or has no hidden series, ApexCharts takes an internal fast path: it recomputes axis scales and redraws only the series paths, leaving the grid, axes, legend, and tooltip DOM in place. This happens automatically. For high-frequency updates where the data window is fixed, updateSeries is faster than appendData because appendData never takes this path.

updateSeries also resets any zoom-adjusted axis bounds, so the chart returns to its natural scale on each call.


appendData

Pushes new data points onto the end of each existing series. The existing data is preserved.

chart.appendData([
  { data: [{ x: Date.now(), y: 74 }] },  // appended to series[0]
  { data: [{ x: Date.now(), y: 51 }] }   // appended to series[1]
])

The argument is an array indexed to match the existing series. Pass null at a position to skip that series:

// Chart has three series; skip series[1]
chart.appendData([
  { data: [{ x: Date.now(), y: 74 }] },
  null,
  { data: [{ x: Date.now(), y: 31 }] }
])

For a real-time chart with a fixed visible window, keep the data array from growing unbounded. Once the window is full, updateSeries with a sliced array is more efficient:

async function push(chart, value) {
  const data = chart.w.config.series[0].data
  const maxPoints = 30

  if (data.length >= maxPoints) {
    await chart.updateSeries([
      { data: [...data.slice(1), { x: Date.now(), y: value }] }
    ])
  } else {
    await chart.appendData([{ data: [{ x: Date.now(), y: value }] }])
  }
}

updateOptions

Merges a partial config object into the existing chart config and re-renders. Only the keys you provide are changed.

chart.updateOptions({
  xaxis: { categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May'] },
  title: { text: 'Monthly Revenue 2026' }
})

When you need to change both data and config at the same time, pass series inside updateOptions rather than making two separate calls:

chart.updateOptions({
  series: [{ name: 'Revenue', data: newData }],
  xaxis: { categories: newCategories },
  colors: ['#00E396']
})

When series is included this way, each incoming series object is merged with the existing series at the same index. Properties you omit — name, color, type — are preserved from the existing series config.


Real-time streaming pattern

const chart = new ApexCharts(document.querySelector('#chart'), {
  chart: {
    type: 'line',
    animations: {
      enabled: true,
      easing: 'linear',
      dynamicAnimation: { speed: 1000 }
    },
    toolbar: { show: false }
  },
  series: [{ name: 'Sensor', data: [] }],
  xaxis: { type: 'datetime', range: 30_000 },
  yaxis: { min: 0, max: 100 }
})

await chart.render()

const id = setInterval(() => {
  chart.appendData([{
    data: [{ x: Date.now(), y: Math.random() * 100 }]
  }])
}, 1000)

// On teardown:
// clearInterval(id)
// chart.destroy()

Setting xaxis.range to a fixed millisecond window keeps the visible area constant as new points arrive. ApexCharts trims points outside the range automatically.

For loading chart data from a REST API after initial render, see Update Charts from JSON API & AJAX.


Framework usage

Pass series and options as props. The wrapper compares them with deep equality and calls updateSeries or updateOptions as appropriate.

import ReactApexChart from 'react-apexcharts'
import { useState, useEffect } from 'react'

function SalesChart() {
  const [series, setSeries] = useState([{ name: 'Sales', data: [] }])

  useEffect(() => {
    fetch('/api/sales')
      .then(r => r.json())
      .then(data => setSeries([{ name: 'Sales', data }]))
  }, [])

  return (
    <ReactApexChart
      type="bar"
      height={350}
      series={series}
      options={{ chart: { id: 'sales' } }}
    />
  )
}

For imperative updates from polling loops or WebSocket handlers, use the chartRef prop:

const chartRef = useRef(null)

// In an interval or event handler:
chartRef.current?.appendData([{ data: [{ x: Date.now(), y: value }] }])

// In JSX:
<ReactApexChart chartRef={chartRef} ... />

Always clear intervals in the useEffect cleanup to avoid calling update methods after the component unmounts and the chart is destroyed.