Installation
Theme
Mode

Data display

Skeleton

A loading placeholder that traces the shape of what’s coming — same dimensions, same rhythm — so the page doesn’t reflow when data lands. Presets mirror the Card, Table, and list layouts already in the system.

$npx shadcn@latest add @orbit/skeleton

Loading swap

The skeleton holds the exact silhouette of the table that replaces it, so nothing shifts when data arrives.

Loading…

Shape-matched presets

Each preset reproduces the silhouette of a real component, so the swap to live content is seamless.

card — badge, title, two lines, avatar footer
list — avatar rows (rows defaults to 4)
table — header plus data rows (rows defaults to 5)
profile — centered avatar, name, role, two actions
text — heading and body lines for freeform copy

Shimmer, pulse, or static

Shimmer sweeps a highlight across the shapes; pulse fades the whole block; static is motionless. Under prefers-reduced-motion the global guard freezes both animated modes.

shimmer

pulse

static

Primitive

Size the raw block with width/height (numbers are px), round it with radius or circle, and compose custom layouts when no preset matches.

Props

Skeleton (primitive)

PropTypeDefaultDescription
animation"shimmer" | "pulse" | "none""shimmer"Highlight sweep, opacity fade, or static. 1.5s ease-in-out loop.
width / heightnumber | stringBlock size — numbers are px, strings pass through (e.g. "62%").
radiusnumber | string6Corner radius override (4–6px for lines, 999 for pills).
circlebooleanfalseFully round — avatars and dots.
…restHTMLAttributes<HTMLSpanElement>Forwarded to the root <span> (aria-hidden), including ref.

SkeletonCard · SkeletonList · SkeletonTable · SkeletonProfile · SkeletonText

PropTypeDefaultDescription
animation"shimmer" | "pulse" | "none""shimmer"Passed down to every block in the preset.
rowsnumber4 / 5SkeletonList (4) and SkeletonTable (5) only — row count; keep it close to the real data.
linesnumber3SkeletonText only — body line count under the heading.
headingbooleantrueSkeletonText only — show the wider 13px heading line.
…restHTMLAttributes<HTMLDivElement>Forwarded to the preset root <div> (aria-hidden), including ref.

Usage

Do

Match the real layout

Use the preset that mirrors what’s loading so nothing shifts when content arrives.

Don’t

Show it for instant loads

Under ~300ms a skeleton just flickers; render nothing or a spinner instead.

Do

Keep counts realistic

Roughly as many rows as you expect — not three when the list usually has twenty.

Don’t

Animate forever

If data is slow, swap to a message after a few seconds rather than shimmering indefinitely.

Keyboard & accessibility

Skeletons are decoration: every root renders aria-hidden so screen readers skip them. Put role="status" and aria-busy on the live region that swaps between skeleton and content (see the loading-swap demo) so the busy state is announced once. The static animation — and the global prefers-reduced-motion guard — keep the placeholders motionless for people who opt out of animation.