Performance
vccs charts are reactive — they re-render when data or props change. For most use cases, performance is excellent out of the box. For large datasets or frequent updates, these tips help keep things smooth.
Use computed for derived data
Avoid computing data inline in templates. Use computed to cache derived values:
<script setup>
import { computed } from 'vue'
// Good: computed caches the result
const chartData = computed(() =>
rawData.value.map(d => ({ ...d, total: d.a + d.b }))
)
// Bad: recalculates every render
// :data="rawData.map(d => ({ ...d, total: d.a + d.b }))"
</script>
<template>
<LineChart :data="chartData">
<Line data-key="total" />
</LineChart>
</template>
What to expect
Up to a few hundred bars or a few thousand points, transitions stay smooth. Beyond that, the browser still draws every element on every frame, so frames start to drop on slower devices.
A chart registers each series separately. Charts with well over 100 series (for example 100 funnels) can reach Vue's limit on repeated updates in one tick; combine series into fewer components or split the chart.
Disable animations for large datasets
Each animated element is updated on every frame. With thousands of data points, disable animation:
<Line data-key="value" :is-animation-active="false" />
<Bar data-key="value" :is-animation-active="false" />
<Area data-key="value" :is-animation-active="false" />
Sample large datasets
Rather than rendering 10,000+ points, downsample your data:
<script setup>
import { computed } from 'vue'
const sampledData = computed(() => {
const data = rawData.value
if (data.length <= 500)
return data
const step = Math.ceil(data.length / 500)
return data.filter((_, i) => i % step === 0)
})
</script>
Update data the way you like
Charts track reactive() and ref() data deeply. Array replacement, push, splice and row-field edits update the chart. This includes nested path or function accessor values, array-valued rows and edits inside a hierarchy. For very large or streaming datasets, keep plain rows in a shallowRef and assign a new array per update. A shallowRef does not make those rows reactive, so in-place edits to them need an array replacement to notify the chart.
Hovering is cheap: moving the pointer recomputes only tooltip state, never bar, line or axis geometry.
Unwrap reactive proxies for D3
D3 scale functions don't work with Vue's reactive Proxy objects. Use toRaw() when passing data to D3:
<script setup>
import { toRaw } from 'vue'
// When working with custom scales or D3 utilities
const rawEntry = toRaw(entry)
</script>
This is handled internally by vccs, but keep it in mind if you're using D3 utilities directly alongside your charts.
Avoid unnecessary re-renders
- Stable data references: Don't create new arrays/objects on every render. Use
reforcomputedto maintain stable references. - Key prop: When dynamically switching between charts, use
:keyto control component identity and avoid stale state. - Debounce updates: If data changes rapidly (e.g., real-time feeds), debounce updates to avoid overwhelming the renderer.