Gabriel Caiana

Components for Debug and Rendering with DevOnly and ClientOnly in Nuxt 3


Table of contents
  1. DevOnly Components
  2. ClientOnly Components
  3. Client Component Care

DevOnly Components

Sometimes it is necessary to display additional debug information or metadata during development, but without including it in the final production release. An example of this is using the DevOnly component to switch between test accounts, quickly update values ​​in the database, or modify other settings directly in the development environment, without allowing end users to access these functionalities.

The DevOnly component works as expected: everything you wrap inside it will only be available in the development build. See an example of use in the layout:

// layouts/default.vue
<template>
  <div>
    <DevOnly>
      <DevAccountSwitcher />
    </DevOnly>
    <slot />
  </div>
</template>

You can also use the #fallback slot, which renders content only in production builds, if this functionality is required:

// layouts/default.vue
<template>
  <div>
    <DevOnly>
      <DevAccountSwitcher />
      <template #fallback>
        <div>This is rendered only in the production build.</div>
      </template>
    </DevOnly>
    <slot />
  </div>
</template>

ClientOnly Components

You can set a section of your component to render only on the client side using the <ClientOnly> component. This is useful when a piece of content should not be rendered on the server, improving performance by not including that piece in the server build.

See how to use it:

<template>
  <div>
    <p>A regular component rendered on the server and client.</p>
    <ClientOnly>
      <p>But this part shouldn't be rendered on the server</p>
      <WillBreakOnTheServer />
    </ClientOnly>
  </div>
</template>

Content within the default ClientOnly slot is removed from the server build, helping performance. We can also specify a #fallback slot that will be rendered on the server, which is useful for including a loading state to be displayed during hydration:

<template>
  <div>
    <p>A regular component rendered on the server and client.</p>
    <ClientOnly>
      <p>But this part shouldn't be rendered on the server</p>
      <WillBreakOnTheServer />
      <template #fallback>
        <Spinner>Just give me a moment while I load some things.</Spinner>
      </template>
    </ClientOnly>
  </div>
</template>

Client Component Care

Client components are useful when used in conjunction with server components or in isolation, using the suffix .client.vue. However, it is important to remember that because Nuxt wraps these components with <ClientOnly>, they must be imported automatically or manually through #components. Otherwise, they will be imported as regular Vue components.

Another point to consider is that because these components are not rendered on the server, there is no HTML available until they are assembled and rendered. This means we need to wait a render cycle before accessing the template:

// ~/components/CoolComponent.client.vue
<template>
  <div ref="container">
    <!-- Do some cool stuff here -->
  </div>
</template>
<script setup>
const container = ref(null);
onMounted(async () => {
  // Nothing has been rendered yet
  console.log(container.value); // -> null

  // Wait for one render cycle
  await nextTick();

  // Now we can access it!
  console.log(container.value); // -> <div ...>
});
</script>

Did you like the content? To further deepen your knowledge, I recommend reading the official guide on <DevOnly /> and <ClientOnly />. Check it out at: Nuxt DevOnly Guide and Nuxt ClientOnly Guide.

If you have questions or want to exchange development ideas, feel free to contact me on LinkedIn or GitHub. It will be a pleasure to continue the conversation!