SSR
How skeletonization behaves with server-side rendering and hydration.
SSR
How it works
The automatic engine measures and rewrites the DOM, which only exists on the client. So skeleton painting runs on the client, after hydration. The server and the client's initial render are identical, which means no hydration mismatch.
In practice:
- The server renders your real markup (or the loading branch).
- The client hydrates that exact markup.
- After mount, if the scope is enabled, the engine paints bones.
- When loading completes, the originals are restored in place.
Avoiding a flash
Bind :enabled to your loading state so the skeleton only activates while data is pending:
<script setup lang="ts">
// Lazy composables return immediately with `pending: true`, so the skeleton
// shows right after hydration and disappears when data arrives.
const { data, pending } = await useLazyFetch('/api/dashboard')
</script>
<template>
<Skeletonizer :enabled="pending">
<Dashboard :data="data" />
</Skeletonizer>
</template>
Manual primitives during SSR
The Skeleton* primitives are plain markup, so they are server-rendered. Use them when you want
the skeleton visible in the initial HTML (e.g. above-the-fold placeholders):
<template>
<div v-if="pending">
<SkeletonCard media avatar :lines="3" />
</div>
<ArticleCard v-else :article="data" />
</template>
Nitro & accessibility
The module is Nitro-compatible and the runtime plugin is SSR-safe (it no-ops DOM access on the
server). Painted bones are aria-hidden and active hosts get aria-busy.