Skeleton
A placeholder shaped like the thing that is coming. Eighteen files reference one of three separate skeleton modules today.
A story list loading
HTML
<div style="max-width:30rem">
<div style="padding-bottom:var(--p234-space-5);border-bottom:1px solid var(--p234-color-rule);margin-bottom:var(--p234-space-5)">
<span class="p234-skeleton p234-skeleton--line p234-skeleton--short"></span>
<span class="p234-skeleton p234-skeleton--headline"></span>
<span class="p234-skeleton p234-skeleton--line"></span>
<span class="p234-skeleton p234-skeleton--line p234-skeleton--medium"></span>
</div>
<div>
<span class="p234-skeleton p234-skeleton--line p234-skeleton--short"></span>
<span class="p234-skeleton p234-skeleton--headline"></span>
<span class="p234-skeleton p234-skeleton--line p234-skeleton--medium"></span>
</div>
</div>An article loading, with its hero
HTML
<div style="max-width:30rem">
<span class="p234-skeleton p234-skeleton--line p234-skeleton--short"></span>
<span class="p234-skeleton p234-skeleton--headline"></span>
<span class="p234-skeleton p234-skeleton--figure" style="margin:var(--p234-space-5) 0"></span>
<span class="p234-skeleton p234-skeleton--line"></span>
<span class="p234-skeleton p234-skeleton--line"></span>
<span class="p234-skeleton p234-skeleton--line p234-skeleton--medium"></span>
</div>Shaped like what is coming
A skeleton is worth having only if it matches the layout that replaces it. A generic grey box that gives way to a different shape makes the page jump, which is worse than showing nothing. The figure placeholder uses the same 16:9 token the real image does, so the space is already right.
A skeleton, not a spinner
A spinner says "wait". A skeleton says "here is what is arriving, and roughly how much of it". On a slow phone connection that difference is most of the perceived speed.
The trap worth knowing
A route-level loading file that renders above a notFound() or a redirect makes a page that should be a 404 return 200 with a skeleton in it. Search engines read that as a soft 404. This has already cost indexing on this site once, so the rule is: loading placeholders go inside the part of the page that is fetching, below any decision about whether the page exists at all.
Rules
- Match the real layout: the same number of lines, the same widths, the same rules between items.
- Vary the line widths. Equal-length grey bars read as a loading graphic, not as text.
- Mark the region
aria-busy="true"and hide the bars themselves from screen readers.
- Do not show a skeleton for something that arrives in under about 200ms. The flash is the slowest-feeling part.
- Do not animate under
prefers-reduced-motion. The placeholder stays, the shimmer goes. - Do not use one for content rendered on the server. It is already there.
Tokens this component reads
--p234-color-paper-sunk--p234-color-rule--p234-radius-sm--p234-figure-ratio--p234-space-3--p234-space-4