ClientOnly — Skip SSR for a Subtree

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.

4 min read Level 2/5 #nuxt#client-only
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 canvas during initialization.
  • Map libs (Leaflet, Mapbox GL) — need window to 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 →