components
Skeleton
Prefer skeletons over spinners for perceived performance. Shape them to match the content that's about to arrive — identical rows pass the eye test, generic grey bars don't.
Read the full specPrimitives
Three base shapes — line, circle and rectangle. Compose these into any layout.
tsx
<Skeleton className="h-4 w-48" /> {/* line */}
<Skeleton className="size-10 rounded-full" /> {/* avatar */}
<Skeleton className="h-32 w-full" /> {/* block */}Card placeholder
A shape-matched card skeleton. Arranges the same way the finished card will, so the layout doesn't jump on load.
tsx
<div className="rounded-md border border-border p-4">
<Skeleton className="h-4 w-32" />
<Skeleton className="mt-2 h-3 w-48" />
<Skeleton className="mt-4 h-20 w-full" />
</div>Row list
Three rows stacked. Render the same count as the real list if you know it, otherwise pick 3–5.
tsx
{Array.from({ length: 3 }).map((_, i) => (
<div key={i} className="flex items-center gap-3 py-3">
<Skeleton className="size-8 rounded-full" />
<Skeleton className="h-4 flex-1" />
<Skeleton className="h-4 w-16" />
</div>
))}Reduced-motion contract
The shimmer disables automatically when the OS requests reduced motion — tokens.css handles that so you don't need a prop. The placeholder stays visible, just static.
Enable reduced motion in your OS settings and reload — the skeletons above stop shimmering and become a flat muted block.