Skeleton
Skeleton displays placeholders while content is loading, matching the actual layout structure to prevent layout shifts and improve perceived performance.
Playground
<Skeleton count={3} />Variants
Features
Animation
Two animation types — pulse fades in/out, wave sweeps left to right with a shimmer effect
<Skeleton animation="pulse" />
<Skeleton animation="wave" />
<Skeleton animation={false} />Composition
Compose multiple Skeletons to replicate complex layouts like cards and lists.
<div className="flex items-start gap-4">
<Skeleton variant="circular" />
<div className="flex-1 space-y-2">
<Skeleton className="w-1/3" />
<Skeleton />
<Skeleton className="w-4/5" />
</div>
</div>Count
count auto-generates text lines — the last line is shorter, with natural width variation per line
<Skeleton count={3} />
<Skeleton count={5} animation="wave" />Loading Wrapper
loading prop enables conditional rendering — true shows Skeleton, false renders children
<Skeleton loading={isLoading} variant="circular">
<Avatar src={user.avatar} />
</Skeleton>Custom Sizing
width, height, radius props allow precise sizing
<Skeleton width={200} height={12} />
<Skeleton variant="circular" width={64} height={64} />
<Skeleton variant="rectangular" height={120} radius={16} />API
Component Structure
Skeleton— Pure ReactStandalone component — no sub-components
Props
variant"text""text" | "circular" | "rectangular"Shape variant (text: rounded lines, circular: circle, rectangular: rectangle)
animation"pulse""pulse" | "wave" | falseAnimation type (pulse: fade in/out, wave: shimmer, false: static)
countundefinednumberNumber of text lines (text variant only — with automatic width variation)
loadingundefinedbooleanConditional loading (true: show Skeleton, false: render children)
widthundefinedstring | numberCustom width
heightundefinedstring | numberCustom height
radiusundefinedstring | numberCustom border-radius
childrenundefinedReactNodeContent to render when loading is false
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
variant | "text" | "circular" | "rectangular" | "text" | Shape variant (text: rounded lines, circular: circle, rectangular: rectangle) |
animation | "pulse" | "wave" | false | "pulse" | Animation type (pulse: fade in/out, wave: shimmer, false: static) |
count | number | undefined | Number of text lines (text variant only — with automatic width variation) |
loading | boolean | undefined | Conditional loading (true: show Skeleton, false: render children) |
width | string | number | undefined | Custom width |
height | string | number | undefined | Custom height |
radius | string | number | undefined | Custom border-radius |
children | ReactNode | undefined | Content to render when loading is false |
className | string | undefined | Additional CSS classes |
Anatomy
Best Practices
Do
- ✓Match skeleton shape and size to the actual content layout
- ✓Use circular for avatars, text for copy, rectangular for images
- ✓Combine with loading.tsx for initial page renders
Don't
- ✕Don't use skeletons that differ significantly from the actual layout
- ✕Don't use Skeleton for instantly-loaded data — causes unnecessary flicker
- ✕Use Spinner instead of Skeleton for indeterminate waiting states
Accessibility
ARIA / WCAG
aria-hidden="true"applied automatically- Hidden from screen readers as a decorative element
- Set
aria-busyon the parent container prefers-reduced-motionsupported