A placeholder that mimics the shape and layout of content before it arrives, giving users a sense of what is coming without a blank screen or a spinning indicator.
Skeleton loaders address a specific UX problem: blank screens feel broken, and a spinning indicator tells users nothing about what they're waiting for. A skeleton does both — it signals "loading" and shows roughly what the page will look like when data arrives. This reduces perceived wait time even when the actual load time is identical.
The visual language is intentional. Shapes — rectangles for text lines, circles for avatars, wider blocks for images — correspond to real content regions. The shimmer animation (a highlight sweeping left to right) adds the sense of progress without implying a fixed countdown. These two things together communicate "the page is alive and building itself."
Skeleton loaders make the most sense when loading time is unpredictable or over roughly 300ms, and when the shape of the incoming content is known in advance. They're well-suited for feeds, dashboards, profile pages, and search results — layouts you can sketch before the data arrives.
They're a poor choice when loading is fast enough that the skeleton flickers in and out before anyone reads it (under ~100ms it's worse than nothing), or when the incoming content has no predictable structure.
Granularity matters. A skeleton that's too precise (matching every detail of the real layout) looks eerie when the real content finally renders at a different size. Approximate the layout rather than replicating it. One rectangle per line of text, one block per image — that's enough to set expectations without creating a mismatch.
For accessibility, skeleton containers should have aria-busy="true" and aria-label="Loading" so screen readers announce the loading state rather than reading out a list of empty shapes.