Skip to content
UX Atlas
Plate PAT·012 · PatternGrid F2 · Rev. 2026-09-14

Skeleton Screens

Grey placeholders in the shape of the content that is coming, which reads as progress rather than as waiting.

PatternMixed evidenceIntermediate

Definition

A skeleton screen shows a simplified wireframe of the incoming layout while data loads. It sets expectations about structure and avoids the layout shift that occurs when content replaces a spinner.

Mixed evidence. Real research exists, but findings conflict, effect sizes are small, or the jump from the lab to interface design is contested.

On this plate
  1. 1Definition
  2. 2Doing it properly
  3. 3Skeletons that help
  4. 4What deserves a placeholder, and what does not
  5. 5Announcing it, and respecting reduced motion
  6. 6In practice
  7. 7Commonly confused with
  8. 8Local chart
  9. 9Continue from here

Reach for this when

  • Content loads take one to five seconds.
  • The layout of the result is known in advance.

Common mistakes

  • Skeletons that do not match the content.
  • Distracting shimmer animation.
  • Skeletons for very short waits.
  • Treating skeletons as a performance fix, when they change the experience of the wait rather than its length.

Doing it properly

  • Match the real layout in dimensions and position, or the shift on arrival defeats the purpose.
  • Keep the shimmer subtle, or remove it entirely under prefers-reduced-motion.
  • Do not skeleton for waits under about 300ms.
  • Mark the skeleton container aria-busy and hide the placeholder shapes from assistive technology.
  • Show a realistic number of placeholder items, not always exactly three.

Skeletons that help

Do

  • Generate them from the same layout code as the content, so they cannot drift.
  • Match the number of items to what usually arrives.
  • Keep the shimmer slow and low contrast, or leave it static.
  • Fall back to an error state when the request fails, rather than shimmering forever.

Do not

  • Use a skeleton for a wait under a few hundred milliseconds.
  • Animate aggressively, which is both distracting and a reduced-motion concern.
  • Show skeletons for content that will not appear because the set is empty.
  • Build a hand-drawn skeleton that stops matching the component a month later.

What deserves a placeholder, and what does not

A skeleton is a promise about what is arriving. Promising the wrong thing is worse than promising nothing.

  • Text blocks and list rows: yes, at the real line length, with the last line shorter.
  • Images and avatars: yes, at the exact aspect ratio, which is most of the layout-shift win.
  • Numbers, totals and counts: usually not. A grey bar where a figure belongs reads as a number the reader cannot make out.
  • Charts: not as a fake chart. Reserve the space and say the data is loading, or readers will try to read the placeholder.
  • Anything that may not arrive: no. A skeleton for a section that turns out to be empty promised content that never existed.
  • Controls and buttons: no. Render them disabled instead, so the reader can see what will be possible.

Announcing it, and respecting reduced motion

css
/* <div class="card" aria-busy="true">
     <div class="skeleton-line" aria-hidden="true"></div>
   </div> */

.skeleton-line {
  height: 1rem;
  border-radius: 4px;
  background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.6s ease-in-out infinite;
}

@keyframes skeleton-shimmer {
  to { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton-line {
    animation: none;
    background: #eee;
  }
}

The container carries the busy state, the shapes are hidden from assistive technology, and the shimmer stops entirely for readers who asked for less motion.

In practice

  1. i.

    Skeleton driven by the same layout code

    Component libraries

    Generating the skeleton from the same layout primitives as the content guarantees the shapes match and keeps them matching as the component changes.

  2. ii.

    Shimmering forever

    Failure looks like loading

    A request that errored left the skeleton in place indefinitely, so readers waited, refreshed and eventually left. Every loading state needs a defined failure path.

Commonly confused with

Close neighbours that get substituted for each other. The difference is stated first, then the condition that picks one over the other.

vs.

Progress Indicator

Component

A measure of how much of a known task is done, or that work is ongoing.

Key difference

A skeleton says what is coming and where it will sit. A progress indicator says how far along the work is. Only use progress when you can honestly measure it; a skeleton never claims to know.

Use Skeleton Screens when

  • The layout is known before the data is.
  • The wait is short and the page would otherwise shift.
  • Several regions arrive independently.

Use Progress Indicator when

  • Completion is genuinely measurable, as in an upload or an import.
  • The wait is long enough that the reader needs an estimate.
  • There is no layout to stand in for, only work to report.

Learn about Progress Indicator

Local chart

PAT·012 · Region III · Grid F2

This entry and the entries it is stated to connect with. Each line is labelled with what the connection means.

Each link says what the connection is, so you can tell a principle from an alternative from a thing people mix this up with.

Principles behind this

The reasoning this solution is an application of.

Depends on

Get these right first, or this one will not hold.

Often used with

These usually appear in the same screen or the same decision.

Short definition

The one-paragraph version, for when that is all you need.

Related concept

Connected closely enough to change how you apply this.