vaiperui

components / skeleton

Skeleton

Loading placeholder shaped like the content: text lines, title, avatar, rectangle or card.

How to use

Decorative (`aria-hidden`), so set `aria-busy` on the container while loading. The pulse stops under `prefers-reduced-motion`. Resize with `width`/`height` or `className`; `lines` draws several text lines, the last one shorter.

preview
Skeleton.tsx
import { Skeleton } from "vaiperui";
<div aria-busy={loading}>
<Skeleton variant="avatar" />
<Skeleton variant="title" width="60%" />
<Skeleton lines={3} />
</div>

Variants

preview
text
title
avatar
rectangle
card
variants.tsx
<Skeleton variant="text" />
<Skeleton variant="title" />
<Skeleton variant="avatar" />
<Skeleton variant="rectangle" />
<Skeleton variant="card" />

Lines and static

preview
lines={3}
animation="none"
lines.tsx
<Skeleton lines={3} />
<Skeleton animation="none" variant="card" />

Props

PropValuesDefaultDescription
varianttext | title | avatar | rectangle | cardtextShape and default size
linesnumber1text only: number of lines; the last of several is shorter
width / heightnumber | stringper variantCSS size (a number is px)
radiusnone | sm | md | lg | fullper variantOverrides the corner rounding
animationpulse | nonepulsePulse is off with prefers-reduced-motion
(native)HTMLAttributes<HTMLDivElement>nonerole (overrides the default), className, style, aria-*, ref, etc.

Accessibility

  • Every element is `aria-hidden="true"`, so screen readers skip the placeholder entirely.
  • It has no role and no text; it conveys nothing by itself.
  • The pulse animation uses `motion-reduce:animate-none`, so it stops under `prefers-reduced-motion`.
  • Pass `animation="none"` to render a static placeholder.

Keep in mind

  • Skeleton sets no `aria-busy` and announces nothing: put `aria-busy` on the loading container yourself.
  • If users must know loading started or ended, announce it in your own live region (for example a visually hidden `role="status"` text).