Skip to content

Skeleton component

Skeleton is a loading placeholder — either a standalone shaped block or a wrapper that renders its skeleton until `isLoaded` flips, then reveals the real content.

Layout & Display8 propsAccessibility notes

Playground

Change a prop on the right and the preview updates. Switch to Code to copy what you see.

Recipes

Copy-paste patterns for the ways Skeleton actually gets used — each one a real, self-contained example.

Shapes

Three primitive shapes — text, rect, and circle — sized with width/height to match whatever they stand in for.

<Skeleton variant="circle" width={40} height={40} />
<Skeleton variant="rect" width={200} height={120} />
<Skeleton variant="text" width={160} />

Multi-line text

variant="text" with lines renders a stack of text placeholders — the last line comes up short, like a real paragraph.

<Skeleton variant="text" lines={3} />

Wrap until loaded

Wrap real content and flip isLoaded when data arrives — Skeleton shows the placeholder, then reveals the children in place with no layout shift.

<Skeleton isLoaded={!loading}>
  <Avatar src={user.avatar} name={user.name} />
</Skeleton>

Composed card skeleton

Compose the primitives to mirror a real card's layout so the loading state has the same shape as the content it replaces.

<div className="flex gap-3">
  <Skeleton variant="circle" width={44} height={44} />
  <div className="flex-1">
    <Skeleton variant="text" width="60%" />
    <Skeleton variant="text" lines={2} />
  </div>
</div>

Example

skeleton.tsx
<Skeleton isLoaded={!loading}>
  <Avatar src={user.avatar} name={user.name} />
</Skeleton>

<Skeleton className="h-4 w-40" />

Props

The full public API for Skeleton. Every prop is stable and intentionally typed.

Appearance

variant

"text" | "rect" | "circle"

default: rect

Shape of a standalone placeholder.

width

number | string

default: —

CSS length (number → px).

height

number | string

default: —

CSS length (number → px).

radius

OksRadius

default: —

Corner radius override.

Content

lines

number

default: —

variant="text" only — stacked lines, last one shorter.

children

ReactNode

default: —

Wrapper-mode content, hidden behind the placeholder until isLoaded.

Behavior & states

isLoaded

boolean

default: false

Wrapper mode — reveal children when true.

Styling

classNames

{ base?: string }

default: —

Slot class overrides.

Accessibility

  • While loading, the region is marked `aria-busy="true"` so assistive tech knows content is pending.
  • The shimmer animation respects `prefers-reduced-motion` and falls back to a static tint.