A loading placeholder with shimmer or fade variants, plus a transition that crossfades into real content once loading finishes.
Made by AxylInstallation
File Structure
Usage
Basic
Use SkeletonAvatar, SkeletonText, and SkeletonButton for common shapes — they accept all
Skeleton props (SkeletonAvatar/SkeletonButton fix rounded to their own shape, so that
one prop isn't available on those two):
ShimmerSkeleton is an alias for Skeleton (same component, same props) — pick whichever
name reads better at the call site.
Variants
variant="shimmer" (default) sweeps a moving highlight; variant="fade" uses Tailwind
animate-pulse for a calmer opacity pulse — a good fit for large blocks like cover images
or cards:
Customizing colors
There's no color or background prop — Skeleton reads two CSS variables instead:
--sk-muted for the surface fill and --sk-foreground for the shimmer highlight. Both
default to your app's own --muted / --foreground shadcn tokens when present, so it
matches your theme automatically. Override either directly to use different colors:
Plain Tailwind background utilities (bg-accent, etc.) also work for the surface fill since
className is merged last, but the shimmer highlight only follows --sk-foreground — override
both if you want the two to stay in sync.
Transitioning into content
Wrap a placeholder and the real content in SkeletonTransition — when loading flips to
false, the shimmer settles for a beat, then the skeleton and content crossfade:
Size skeleton to match content
While both are mounted, children is absolutely positioned over skeleton, which drives
the wrapper's height. If the two differ a lot in size, the layout snaps to the content's
real height the instant the crossfade finishes. Keep skeleton close to the real content's
dimensions to avoid a visible jump.
Props
Skeleton
| Prop | Type | Default |
|---|---|---|
variant? | "shimmer" | "fade" | "shimmer" |
duration? | number | 1.6 (shimmer) / 2.4 (fade) |
rounded? | "none" | "sm" | "md" | "lg" | "full" | "md" |
animate? | boolean | true |
decorative? | boolean | true |
label? | string | null | - |
SkeletonTransition
| Prop | Type | Default |
|---|---|---|
loading? | boolean | - |
skeleton? | ReactNode | - |
children? | ReactNode | - |
className? | string | - |
When to Use
Use Skeleton for content that loads asynchronously and has a roughly known shape ahead of
time — profile cards, list rows, article previews. It reduces perceived load time better
than a spinner because it previews the coming layout. Reach for SkeletonTransition whenever
the loading state and the real content live in the same spot, so the swap reads as one
continuous moment instead of a layout jump.
Accessibility
By default, Skeleton is aria-hidden — a purely decorative placeholder alongside content
that's coming. Set decorative={false} to announce it as a live loading status
(role="status", aria-live="polite") when there's no other loading indicator on the page.
When prefers-reduced-motion: reduce is set, shimmer animation stops (injected CSS is
disabled), fade uses motion-reduce:animate-none, and SkeletonTransition skips its settle
delay and crossfades immediately.
Built by Axyl. A motion-first component registry for React.
Last updated: 7/3/2026