Advanced Usage
Scopes, per-container control, scoped themes and custom animations.
Advanced Usage
Scopes
<Skeletonizer> can be nested freely. Each instance can override the global state and the animation
for just its subtree:
<Skeletonizer :enabled="globalLoading">
<Header />
<!-- This card uses its own loading flag and a different animation -->
<Skeletonizer :enabled="cardLoading" animation="pulse">
<PromoCard />
</Skeletonizer>
</Skeletonizer>
A scope's enabled prop always wins over the global flag. Omit it to follow the global state.
Per-container control via the composable
Because control is centralised in a reactive store, you can drive skeletons from anywhere — a toolbar, a route guard, a Pinia action:
const { enable, disable, refresh } = useSkeletonizer()
watch(() => route.params.id, async () => {
enable()
await loadData()
disable()
})
Call refresh() after the DOM changes outside Nuxt's reactivity (e.g. a third-party widget mounted
new nodes) to re-scan all hosts.
Scoped themes
Apply a theme to a single subtree without touching the rest of the page:
<Skeletonizer :enabled="pending" :theme="{ baseColor: '#fde68a', highlightColor: '#fffbeb' }">
<PromoBanner />
</Skeletonizer>
Custom animations
Register your own animation at runtime and switch to it:
const { registerAnimation, setAnimation } = useSkeletonizer()
registerAnimation({
name: 'blink',
css: `
@keyframes sk-blink { 0%,100% { opacity: 1 } 50% { opacity: .3 } }
.sk-anim-blink .sk-bone { animation: sk-blink var(--sk-duration) infinite }
`,
})
setAnimation('blink')
See Theming for the full animation reference.
Steering the engine
Use the directives to fine-tune what gets skeletonized:
<Skeletonizer :enabled="pending">
<Logo v-skeleton-keep /> <!-- always real -->
<span v-skeleton-replace="'badge'">{{ status }}</span>
<li v-for="i in items" v-skeleton-union>{{ i.label }}</li>
</Skeletonizer>