Skeleton

Skeleton displays placeholders while content is loading, matching the actual layout structure to prevent layout shifts and improve perceived performance.

3
Variants
2
Animations
Count
Multi-line
Pure
React

Playground

Preview
text × 3pulse
Variant
Animation
Count
<Skeleton count={3} />

Variants

Text
Placeholder for text lines — rounded corners with proportional line height
Default
Circular
Circular placeholder for avatars and icons
Avatar
Rectangular
Rectangular placeholder for images and card areas
Media

Features

Animation

Two animation types — pulse fades in/out, wave sweeps left to right with a shimmer effect

pulse
wave
false (static)
<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

count={3}
count={5} animation="wave"
<Skeleton count={3} />
<Skeleton count={5} animation="wave" />

Loading Wrapper

loading prop enables conditional rendering — true shows Skeleton, false renders children

loading=true
JH
loading=false
<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 React

Standalone component — no sub-components

Props

variant"text"
"text" | "circular" | "rectangular"

Shape variant (text: rounded lines, circular: circle, rectangular: rectangle)

animation"pulse"
"pulse" | "wave" | false

Animation type (pulse: fade in/out, wave: shimmer, false: static)

countundefined
number

Number of text lines (text variant only — with automatic width variation)

loadingundefined
boolean

Conditional loading (true: show Skeleton, false: render children)

widthundefined
string | number

Custom width

heightundefined
string | number

Custom height

radiusundefined
string | number

Custom border-radius

childrenundefined
ReactNode

Content to render when loading is false

classNameundefined
string

Additional CSS classes

Anatomy

Text
1
Circular
2
Rectangular
3
Wave Overlay
W
1
Text
h-4 rounded-md
2
Circular
w-10 h-10
3
Rectangular
w-full h-24
W
Wave
shimmer overlay

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-busy on the parent container
  • prefers-reduced-motion supported