Skeleton
A pulsing placeholder that holds the shape of content while it loads — one div, sized and shaped by utility classes.
- Status
- Since
0.1.0- Accessibility pattern
- decorative placeholder
Last updated
Install
Configure registry credentials first using the Quickstart. Before copying this item or any transitive registry dependency, verify the signed manifest and save the exact bytes for Skeleton before installation. Retain the digest printed by this command.
pnpm exec vegastack-design verify skeletonInstall with the pinned shadcn CLI. Bare shadcn does not verify VegaStack signature or integrity.
pnpm dlx shadcn@4.21.0 add @vegastack/skeletonRun the exact offline --post-write command printed by the preflight, using its saved item path and independently retained --expected-integrity digest. Use the saved preflight for every transitive registry dependency too; stop on any mismatch before using the components.
Usage
import { Skeleton } from "@/components/ui/skeleton";
<Skeleton className="h-[20px] w-[100px] rounded-full" />;Anatomy
Examples
Avatar
Give the media block the avatar's own size and radius so the layout does not jump when the image arrives.
Card
Skeletons sit inside the real container. Keep the card, the header and the content; replace only what is still loading.
Text
Stack line skeletons at the line height of the copy they stand in for, and cut the last one short — a full-width final line reads as a block, not as text.
Form
Mirror the label/control rhythm of the form so the fields land where the skeleton put them.
Table
Repeat one row shape for as many rows as the first page will hold. Keep the count fixed — a skeleton whose row count moves is a second layout shift.
RTL
The placeholder is direction-agnostic; sizing comes from the utilities you pass, and the surrounding flex or grid flips with the document.
Lines of text
SkeletonText sketches a few lines while text loads: text — 16px bars of varied widths 12px
apart — or rows — full-width 20px bars 8px apart, for a list. It is hidden from assistive
technology; name the loading region around it.
<div role="status" aria-label="Loading">
<SkeletonText lines={3} />
</div>API Reference
Skeleton adds no props of its own — it accepts everything the underlying element or Base UI primitive accepts (className, ref, ARIA attributes, event handlers).
Accessibility
- A skeleton is decorative: it carries no role, no text and no accessible name, so screen readers skip it rather than announcing a wall of empty boxes.
- Announce the loading state once, on the region that owns it —
aria-busy="true"on the container, or a live region for a longer wait — rather than on each placeholder. - The pulse is
animate-pulse, and the global reduced-motion reset inbase.cssstops it for anyone who asks for less motion. The placeholder stays visible. - The fill is
bg-muted, which is a surface rather than text, so it carries no contrast obligation of its own.
| Contract | States tested |
|---|---|
| Behaviour | default, loading |
| Accessibility | busy |
| Visual | default, loading |
Do / Don't
Deviations
Upstream's file plus packages/ui/upstream/patches/skeleton.patch. Every hunk:
- DOC-2 —
cnis imported from@vegastack/design. Nothing else differs: the element, thedata-slotand theanimate-pulse rounded-md bg-mutedrecipe are upstream's.