Skeleton
Holds the place of content while it loads.
Installation
pnpm dlx shadcn@latest add @wisty/skeleton
The CLI installs the Radix or Base UI file to match your project.
If your project doesn't have Wisty UI's theme yet, add it once with npx shadcn@latest add @wisty/cosmos. Until then, components keep shadcn's default look. Installation has the details.
When to use
Use it
- Content takes more than about 300ms to load and you know its shape, like a list, a card or a record's fields.
- A whole page or region loads at once, where many spinners would be noisy.
Don't use it
- The wait is under about 300ms. Show the content when it arrives.
- An action is running, like a save, or you don't know the content's shape. Use Spinner.
- A part of the page only shows when it has data. Don't show a stencil for it.
Usage
Size each Skeleton with classes to match the content it stands in for, and mark the loading region with aria-busy="true".
import { Skeleton } from "@/components/ui/skeleton"<div aria-busy="true">
<Skeleton className="h-4 w-3/5" />
</div>API reference
Skeleton
Also takes every <div> (opens in a new tab) prop.
SLDS mapping
SLDS has no skeleton classes. Its Loading pattern calls these stencils, and the block is its shade surface, slds-theme_shade.
Known gaps:
- SLDS says stencils fade in and cross-fade to the content. Do that yourself if you need it.
- SLDS suggests about 20 fading stencil rows for a long table. There's no table stencil; build the rows from skeletons.
Accessibility
- A skeleton has no role, so screen readers skip it. Remove
aria-busyfrom the region when the content arrives. - For a long wait, also say so in text, like a visually hidden "Loading contacts".
Motion
- The skeleton pulses in every theme. With reduced motion it holds still, so don't animate it yourself.
Rules
- Keep heights consistent, and use percent widths or
w-fullso stencils fill their container. - Keep text that's known before the data arrives, such as a card's title, as real text.
- Don't draw buttons as skeletons.