Skeleton

A skeleton stands in for content while it loads. On a static site that is rarer than in an app — the HTML is already there — but it is what an island’s fallback should usually be, and what a client-rendered region shows before its data lands.

Skeletons are decorative: each one is aria-hidden, so a screen reader is not read a list of empty boxes.

Shapes

stack({ gap: 'md' },
  skeleton({ height: '2.5rem' }),
  skeleton({ variant: 'text', width: '70%' }),
  skeleton({ variant: 'circle', width: '3rem', height: '3rem' }),
)

Text

lines renders a paragraph’s worth, with the last line short so it reads as prose rather than a block.

stack({ gap: 'lg' },
  skeleton({ lines: 2 }),
  skeleton({ lines: 4 }),
)

In the shape of the real thing

A skeleton is most convincing when it matches the layout it replaces — same card, same rows, same sizes.

AL

Ada Lovelace

Pushed 3 commits
grid({ min: '14rem' },
  card(
    cardBody(
      stack({ gap: 'md' },
        stack({ direction: 'row', gap: 'sm', align: 'center' },
          skeleton({ variant: 'circle', width: '2.5rem', height: '2.5rem' }),
          stack({ gap: 'xs', style: 'flex: 1' },
            skeleton({ variant: 'text', width: '60%' }),
            skeleton({ variant: 'text', width: '40%' }),
          ),
        ),
        skeleton({ lines: 3 }),
      ),
    ),
  ),
  card(
    cardBody(
      stack({ direction: 'row', gap: 'sm', align: 'center' },
        avatar({ name: 'Ada Lovelace' }),
        stack({ gap: 'none' },
          text({ variant: 'small' }, 'Ada Lovelace'),
          text({ variant: 'caption', tone: 'muted' }, 'Pushed 3 commits'),
        ),
      ),
    ),
  ),
)

As an island fallback

A server island ships its fallback in the static HTML and swaps in the rendered fragment at request time. A skeleton the same shape as the fragment keeps the page from jumping when it arrives.

Comments

card(
  cardHeader({ title: 'Comments' }),
  cardBody(
    stack({ gap: 'md' },
      skeleton({ lines: 2 }),
      divider({ spacing: 'xs' }),
      skeleton({ lines: 2 }),
    ),
  ),
)

Motion

The shimmer stops for anyone who has asked their system to reduce motion — that is handled in the stylesheet, with no prop to set.

Props

PropTypeDefaultDescription
variant'rect' | 'text' | 'circle''rect'The shape of the placeholder.
widthstringAny CSS width.
heightstringAny CSS height.
linesnumberRender this many text lines, the last one short.