Skeleton
Skeleton is a placeholder to show a loading state and the expected shape of a component.
Usage
import { Skeleton } from '@hareui/vue'<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template> <div class="shadow-panel w-[250px] space-y-5 rounded-lg bg-transparent p-4"> <Skeleton class="h-32 rounded-lg" /> <div class="space-y-3"> <Skeleton class="h-3 w-3/5 rounded-lg" /> <Skeleton class="h-3 w-4/5 rounded-lg" /> <Skeleton class="h-3 w-2/5 rounded-lg" /> </div> </div></template>Anatomy
Skeleton is a single element. Size and shape come from class; the default slot is optional.
ui keys: base.
Examples
Text Content
<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template> <div class="w-full max-w-md space-y-3"> <Skeleton class="h-4 w-full rounded" /> <Skeleton class="h-4 w-5/6 rounded" /> <Skeleton class="h-4 w-4/6 rounded" /> <Skeleton class="h-4 w-full rounded" /> <Skeleton class="h-4 w-3/6 rounded" /> </div></template>User Profile
<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template> <div class="flex items-center gap-3"> <Skeleton class="h-10 w-10 shrink-0 rounded-full" /> <div class="flex-1 space-y-2"> <Skeleton class="h-3 w-36 rounded-lg" /> <Skeleton class="h-3 w-24 rounded-lg" /> </div> </div></template>List Items
<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template> <div class="w-full max-w-sm space-y-4"> <div v-for="index in 3" :key="index" class="flex items-center gap-3"> <Skeleton class="h-10 w-10 shrink-0 rounded-lg" /> <div class="flex-1 space-y-2"> <Skeleton class="h-3 w-full rounded" /> <Skeleton class="h-3 w-4/5 rounded" /> </div> </div> </div></template>Grid
<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template> <div class="grid w-full max-w-xl grid-cols-3 gap-4"> <Skeleton class="h-24 rounded-xl" /> <Skeleton class="h-24 rounded-xl" /> <Skeleton class="h-24 rounded-xl" /> </div></template>Single Shimmer
A synchronized shimmer effect that passes over all skeleton elements at once. Put the container classes below on a parent element (the equivalent of HeroUI's skeleton--shimmer class) and set animation-type="none" on child skeletons.
relative overflow-hidden before:pointer-events-none before:absolute before:inset-0 before:z-10
before:-translate-x-full before:animate-skeleton before:mix-blend-overlay before:content-['']
before:bg-[linear-gradient(90deg,transparent_0%,rgba(255,255,255,0.5)_50%,transparent_100%)]<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template> <!-- HeroUI's `.skeleton--shimmer` parent rule, written as utilities on the container --> <div class="relative grid w-full max-w-xl grid-cols-3 gap-4 overflow-hidden rounded-xl before:pointer-events-none before:absolute before:inset-0 before:z-10 before:-translate-x-full before:animate-skeleton before:bg-[linear-gradient(90deg,transparent_0%,rgba(255,255,255,0.5)_50%,transparent_100%)] before:mix-blend-overlay before:content-['']" > <Skeleton animation-type="none" class="h-24 rounded-xl" /> <Skeleton animation-type="none" class="h-24 rounded-xl" /> <Skeleton animation-type="none" class="h-24 rounded-xl" /> </div></template>Animation Types
Shimmer
Pulse
None
<script setup lang="ts">import { Skeleton } from '@hareui/vue'const types = [ { type: 'shimmer', label: 'Shimmer' }, { type: 'pulse', label: 'Pulse' }, { type: 'none', label: 'None' },] as const</script><template> <div class="grid w-full max-w-xl grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3"> <div v-for="item in types" :key="item.type" class="space-y-2"> <p class="truncate text-xs text-muted">{{ item.label }}</p> <div class="shadow-panel space-y-3 rounded-lg bg-transparent p-4"> <Skeleton :animation-type="item.type" class="h-20 rounded-lg" /> <Skeleton :animation-type="item.type" class="h-3 w-3/5 rounded-lg" /> <Skeleton :animation-type="item.type" class="h-3 w-4/5 rounded-lg" /> </div> </div> </div></template>Customization
Tailwind CSS
<script setup lang="ts">import { Skeleton } from '@hareui/vue'</script><template> <div class="w-[250px] space-y-5 rounded-xl border border-border/80 bg-surface p-4 shadow-sm ring-1 ring-black/5 dark:ring-white/10"> <Skeleton class="h-32 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" /> <div class="space-y-3"> <Skeleton class="h-3 w-3/5 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" /> <Skeleton class="h-3 w-4/5 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" /> <Skeleton class="h-3 w-2/5 animate-shine rounded-lg bg-neutral-200/90 dark:bg-neutral-800/90" /> </div> </div></template>Global theme override
Override the skeleton theme app-wide with createHareUI:
app.use(createHareUI({
ui: {
skeleton: {
slots: {
base: 'bg-surface-secondary/50', // Change base background
},
variants: {
animationType: {
shimmer: { base: 'after:via-surface' }, // Change shimmer gradient color
pulse: { base: 'animate-pulse opacity-75' }, // Customize pulse animation
none: { base: 'opacity-50' }, // Style for static skeleton
},
},
},
},
}))Global Animation Configuration
You can set a default animation type for all Skeleton components in your application by defining the --skeleton-animation CSS variable:
/* In your global CSS file */
:root {
/* Possible values: shimmer, pulse, none */
--skeleton-animation: pulse;
}
/* You can also set different values for light/dark themes */
.light, [data-theme="light"] {
--skeleton-animation: shimmer;
}
.dark, [data-theme="dark"] {
--skeleton-animation: pulse;
}This global setting will be overridden by the animationType prop when specified on individual components. Like HeroUI, the variable is read from :root once per page load, after the component mounts.
Styling Reference
Like HeroUI's, the Skeleton root has no data-slot.
ui key | Description |
|---|---|
base | Base skeleton styles with background and rounded corners |
HeroUI's modifier classes correspond to the animationType prop (HareUI renders utilities, not BEM classes, so style through class, ui or createHareUI):
.skeleton--shimmer→shimmer(default): shimmer animation with a gradient.skeleton--pulse→pulse: Tailwind'sanimate-pulse.skeleton--none→none: static skeleton
Animation
Shimmer Animation
The shimmer effect creates a gradient that moves across the skeleton element:
// theme/skeleton.ts, animationType.shimmer
'after:absolute after:inset-0 after:-translate-x-full after:animate-skeleton after:bg-linear-to-r after:from-transparent after:via-surface-tertiary after:to-transparent after:content-[\'\']'The shimmer animation is defined in the theme using:
@theme inline {
--animate-skeleton: skeleton 2s linear infinite;
@keyframes skeleton {
100% {
transform: translateX(200%);
}
}
}Pulse Animation
The pulse animation uses Tailwind's built-in animate-pulse utility.
No Animation
animationType="none" applies no animation classes.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
as | AsTag | Component | "div" | The element or component to render as. |
animationType | "none" | "shimmer" | "pulse" | - | Animation type. When omitted, the --skeleton-animation CSS variable on :root
(shimmer, pulse or none) decides, falling back to shimmer. |
ui | ComponentSlots<{ slots: { base: string; }; variants: { animationType: { shimmer: { base: string; }; pulse: { base: string; }; none: { base: string; }; }; }; defaultVariants: { animationType: string; }; }> | - | Per-slot class overrides. |
Slots
| Slot | Props | Description |
|---|---|---|
default | any | Optional content rendered inside the placeholder. |

