Skeleton

A placeholder that mirrors content while it loads.

Bone is the skeleton primitive — a single neutral block. Compose bones into the shape of the content that is loading so the layout does not jump when the real data arrives. Reach for it over a spinner when the incoming content has a predictable structure.

<Bone height={12} />
<Bone height={12} width={160} />
<div className="flex items-center gap-2">
<Bone height={32} circle />
<div className="flex-1">
  <Bone height={12} />
</div>
</div>

Each bone takes a height and an optional width — omit width to fill the container. Pass circle for an avatar or icon placeholder; it uses height for both dimensions and renders a full circle.

A bone never shrinks, and one without a width has no intrinsic size, so in a flex row it collapses to nothing. Wrap it in a flex-1 element, as above, or give it an explicit width.

When to use

Use Bone to stand in for content with a predictable structure while it loads, so the layout does not jump when the data arrives.

Use something else when:

  • The structure of the result is unknown. Use Spinner.
  • The region will have nothing in it. Use Empty.

To make a skeleton read as actively loading, layer a Shimmer Overlay on it.

Usage

Do

Match the bones to the real content's dimensions and spacing, so the swap-in is invisible rather than a reflow.

Don't

Fill a screen with dozens of bones. A few that stand in for the primary content read as loading; a full page of them reads as a glitch.

Props

PropTypeDefault
heightrequired

Height in pixels. A `circle` uses it for both dimensions.

TypenumberDefault—
circle

Render a full circle, `height` wide and `height` tall.

TypebooleanDefault—
radius

Corner radius in pixels. Ignored when `circle` is set.

TypenumberDefault6
width

Width in pixels or any CSS length. Omit to fill the container.

Typestring | numberDefault—

Bone also forwards any div props, such as className or data-testid, to its root, and its own classes are kept alongside yours.

Accessibility

A bone is a decorative div with no text or role, so it is skipped by screen readers. Give the loading region a status — for example role="status" with a "Loading" label — so non-visual users learn the content is on its way.

Each bone sweeps a soft highlight across itself on a 1.6s loop, from @base-org/bds-web/styles.css. The sweep stops when the reader prefers reduced motion, leaving a still block.