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/skeletonLoading swap
The skeleton holds the exact silhouette of the table that replaces it, so nothing shifts when data arrives.
Shape-matched presets
Each preset reproduces the silhouette of a real component, so the swap to live content is seamless.
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)
| Prop | Type | Default | Description |
|---|---|---|---|
| animation | "shimmer" | "pulse" | "none" | "shimmer" | Highlight sweep, opacity fade, or static. 1.5s ease-in-out loop. |
| width / height | number | string | — | Block size — numbers are px, strings pass through (e.g. "62%"). |
| radius | number | string | 6 | Corner radius override (4–6px for lines, 999 for pills). |
| circle | boolean | false | Fully round — avatars and dots. |
| …rest | HTMLAttributes<HTMLSpanElement> | — | Forwarded to the root <span> (aria-hidden), including ref. |
SkeletonCard · SkeletonList · SkeletonTable · SkeletonProfile · SkeletonText
| Prop | Type | Default | Description |
|---|---|---|---|
| animation | "shimmer" | "pulse" | "none" | "shimmer" | Passed down to every block in the preset. |
| rows | number | 4 / 5 | SkeletonList (4) and SkeletonTable (5) only — row count; keep it close to the real data. |
| lines | number | 3 | SkeletonText only — body line count under the heading. |
| heading | boolean | true | SkeletonText only — show the wider 13px heading line. |
| …rest | HTMLAttributes<HTMLDivElement> | — | Forwarded to the preset root <div> (aria-hidden), including ref. |
Usage
Match the real layout
Use the preset that mirrors what’s loading so nothing shifts when content arrives.
Show it for instant loads
Under ~300ms a skeleton just flickers; render nothing or a spinner instead.
Keep counts realistic
Roughly as many rows as you expect — not three when the list usually has twenty.
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.