Nuxt and SSR
The server sends each chart at its entrance start: collapsed bars, undrawn lines and transparent cells. After hydration, the entrance plays when the chart is on screen.
Install the module
The module needs Nuxt 4 or later. On Nuxt 3 it stops the build with an error; there, import the components from vccs or use vccs/resolver.
export default defineNuxtConfig({
modules: ['vccs/nuxt'],
})
The module auto-imports every component and every composable (usePlotArea,
useXAxisScale, …). Import types and defineChartComponents from vccs. Components have no
prefix by default. Names such as Tooltip, Legend and Label can conflict with shadcn-vue or Nuxt UI. Set a prefix to avoid these name collisions:
export default defineNuxtConfig({
modules: ['vccs/nuxt'],
vccs: { prefix: 'V' }, // <VBarChart>, <VLine>, ...
})
How server rendering works
Responsive charts reserve their box on the server and stay hidden until the browser measures it. This avoids drawing at the wrong width and then snapping to the real size.
- A chart with numeric
widthandheightrenders exactly at that size. - A chart with only
height(the most common case) uses an initial width of 640 px and fills the container width after measurement. - Without a size, the chart starts at 640 × 360 (or
640 / aspecthigh whenaspectis set).
For a complete chart without JavaScript, use fixed width and height and turn animation off:
<AreaChart :data="data" :width="900" :height="250" :is-animation-active="false">
<Area data-key="desktop" />
</AreaChart>
Disabling animation alone does not reveal an unmeasured responsive chart.
What vccs does for you
- Same first render on server and client. Every chart item registers before any geometry renders, regardless of the order you declare children in. A
<Line>declared before a<Bar>still gets the bar's category padding on the first render. - Entrance after hydration. Hydration preserves the entrance start. The entrance plays when the chart is on screen.
- No text measurement during hydration. Tick sizes are measured after mount, then the layout settles.
- Request isolation. Generated ids use Vue's
useId(), and no chart state lives in module scope.
Client-only charts
You do not need <ClientOnly>. Use it only when the data itself exists only in the browser, for example from localStorage.