For Widgets That Can Only Run in the Browser
ClientOnly — Skip SSR for a Subtree
The ClientOnly component renders its slot only after the client takes over, with an optional fallback shown during SSR.
What you'll learn
- Wrap a browser-only widget with ClientOnly
- Provide a fallback slot to avoid layout shift
- Decide when ClientOnly is the right escape hatch
Some libraries simply cannot run on the server — they touch window, document, or browser-only
APIs the moment they load. <ClientOnly> is Nuxt’s escape hatch for these cases.
Basic Usage
<template>
<ClientOnly>
<Chart :data="data" />
</ClientOnly>
</template> During SSR, Nuxt skips the slot entirely — the server output has nothing where <Chart> would go.
After hydration, the client mounts <Chart> normally.
With a Fallback
A blank space during SSR causes layout shift when the widget mounts. Provide a fallback:
<template>
<ClientOnly>
<Chart :data="data" />
<template #fallback>
<div class="chart-skeleton" />
</template>
</ClientOnly>
</template> The fallback renders on the server (and during the brief moment before the client mounts the real component), keeping the layout stable.
Common Use Cases
- Charting libs (Chart.js, ECharts) — many touch
canvasduring initialization. - Map libs (Leaflet, Mapbox GL) — need
windowto bootstrap. - Drag-and-drop libraries.
- Third-party embeds (Twitter cards, YouTube players in some configurations).
- Code that depends on screen size (
window.innerWidth).
Use Sparingly
Every <ClientOnly> is content that does not exist in the SSR HTML — bad for SEO, bad for perceived
performance. Prefer fixing the mismatch (move logic to onMounted) over reaching for
<ClientOnly> by reflex.
For widgets that legitimately can only run client-side, this is the cleanest pattern available.
Server-Only Components and process.server →