Guides · · Maxwell Prehoda
13 Skeleton Screen Examples From Real Apps (and When a Spinner Wins)
A skeleton screen is a grey, content-shaped placeholder that stands in for a page or component while its data loads. The good ones match the final layout closely, animate gently, and disappear the moment content lands. The bad ones look like a broken page or make a short wait feel longer.
Below are 13 skeleton screens worth studying: the 2013 post that named the pattern, the consumer apps that made it normal, the UI kits most teams actually ship with, and two of ours you can copy as plain HTML and CSS. Every entry has a live demo, what it is best for, and the trap to avoid.
One honest note before the list: skeletons are not automatically better than spinners. A 2017 Viget study found the opposite in one test, and Nielsen Norman Group sets clear time limits on when to use them. Both are covered in the how-to-choose section.
1. Polar, the app that named the pattern: Best for replacing spinners on content-heavy mobile screens
Platform: iOS app (2013), documented by Luke Wroblewski
Luke Wroblewski's September 2013 post Mobile Design Details: Avoid The Spinner is where most people first read the term. He defined a skeleton screen as "essentially a blank version of a page into which information is gradually loaded" and explained that his team used the technique in several places in the Polar polling app to effectively eliminate spinners.
The argument still holds: a spinner draws attention to the fact that you are waiting, while a skeleton puts the focus on the content that is arriving. Each piece that fills in reads as progress.
Our Skeleton Shimmer below is a pure-CSS version of the same idea: three placeholder lines and one light band sweeping across them. Use it as the base layer before you shape anything more specific.
Watch out for: The original Polar app is long gone (its team joined Google in 2014), so treat the post as the reasoning, not as a live reference you can inspect.
2. Facebook's feed and profile skeletons: Best for social feeds where every post has the same shape
Platform: Desktop web app
LogRocket's teardown of the Facebook desktop web app notes that it puts skeletons on nearly every dynamic segment, including the profile screen. Two details make it work: it leaves out secondary elements so the placeholder stays simple, and it staggers the pulse so the blocks do not all flash in unison.
That simplicity is the lesson. A feed placeholder only needs an avatar circle, a name bar, two text lines and a media block. Users read it as "a post goes here" in a fraction of a second, and the real post replaces it without the layout jumping.
Our Social Media Post Skeleton below is a pure-CSS version of the same feed card, with an action bar and a media block that drifts instead of sitting flat.
Watch out for: Pulsing every card in perfect sync turns a long feed into a strobing wall. Offset the delay per card, the way Facebook staggers its pulse.
3. YouTube's video thumbnail placeholders: Best for video grids and media-heavy home screens
Platform: Web and mobile apps
YouTube loads its home screen in stages, and the skeleton technique carries each stage. On the mobile app the video thumbnails start as static grey blocks in the exact 16:9 frame the thumbnail will fill, with title lines beneath.
Holding the aspect ratio is what matters on a video grid. If the placeholder is the wrong height, every row shifts when the thumbnails decode, and the user loses the video they were about to tap.
Our Video Card Skeleton below is a pure-CSS version of the same pattern: a 16:9 block with a play glyph and duration badge, an avatar and two title lines, with a diagonal highlight passing over it.
Watch out for: Thumbnails are images, so set width and height (or aspect-ratio) on the real img too. Otherwise the skeleton is the right size and the loaded card is not.
4. Airbnb's home screen grid: Best for listing and marketplace grids full of photos
Platform: Desktop website
LogRocket points out that the Airbnb desktop site uses a skeleton for every dynamic element on its home screen. The search bar and chrome are there immediately; the listing cards arrive as photo-shaped blocks with text lines under them.
This is the case skeletons were made for: the layout is fixed and known, only the data is late, and the grid is large enough that a single spinner would leave most of the screen empty.
Our Image Gallery Skeleton below is a pure-CSS take on a photo grid. Each tile twinkles on its own offset instead of the whole grid pulsing together.
Watch out for: Photo grids are where Largest Contentful Paint lives. A pretty placeholder does not fix a slow hero image; preload it and size it.
5. Uber's vehicle picker: Best for option pickers where prices or quotes arrive late
Platform: Android app
In the Uber Android app the vehicle options selector shows a skeleton while prices load. LogRocket notes three touches: a wave effect, a placeholder shaped like the vehicle icon rather than a plain box, and a short preloader before the skeleton appears.
The shaped placeholder is the clever part. When the icon slot already looks like a car, the user knows what kind of list is coming before a single word renders.
Our Map Location Skeleton below is a pure-CSS version of a location card in the same spirit: placeholder roads, a pin that drops in with a ripple, then the address lines.
Watch out for: A preloader followed by a skeleton is two loading states in a row. Only do it if the first one is genuinely short, or the user sees the screen change twice before anything useful appears.
6. eBay's synchronized shine: Best for ecommerce search results and product browsers
Platform: Desktop website
On eBay's desktop products screen the header loads instantly and the product browser shows skeletons. LogRocket highlights that the placeholders share one synchronized shine, and that the advanced search starts as a single placeholder that later splits into several.
One shared sweep reads as a single loading region instead of twenty separate ones. It is calmer than per-card pulses on a dense grid, and cheaper, because one gradient moves instead of many.
Our Product Grid Skeleton below is a pure-CSS version of the same trick: one mask gradient on the wrapper sends a single bright wave across every card.
Watch out for: Do not keep the shine running after a timeout. If results fail, swap the skeleton for an error or empty state, not an endless shimmer.
7. DoorDash's left-to-right shimmer: Best for delivery and booking apps with card-style listings
Platform: Mobile and web apps
Nielsen Norman Group uses DoorDash as its example of an animated skeleton: a shimmer that travels left to right across the placeholder cards. It contrasts that with Headspace's static grey blocks, the more common type.
Left to right matches reading direction in left-to-right languages, so the sweep feels like something moving forward rather than an idle glow. Keep it slow: a highlight that crosses a card in roughly a second and a half reads as calm.
Our Skeleton Card below is a pure-CSS version: an avatar, two text lines and an image block under one moving highlight.
Watch out for: NN/g warns that animated skeletons can be distracting and cause accessibility problems. Stop the shimmer under prefers-reduced-motion and fall back to a static tint.
8. MUI Skeleton: Best for React apps already on Material UI
Platform: React, MUI (MIT)
MUI's Skeleton ships four variants (text, circular, rectangular and rounded) and three animation settings: the default pulse, `animation="wave"` for a moving highlight, or `animation={false}` for a static block. A text skeleton sizes itself from the font size, and it can infer its size from a Typography child.
It is the fastest route if your app is already on MUI, because the skeleton picks up your theme and spacing for free.
Our List Skeleton below is a pure-CSS version of a common MUI layout: avatar, name, subtitle and a trailing button per row, pulsing top to bottom.
Watch out for: MUI's docs note the default colour can vanish on dark backgrounds; set the background yourself in dark themes.
9. shadcn/ui Skeleton: Best for Tailwind and Next.js projects using shadcn/ui
Platform: React + Tailwind CSS, copied into your repo
The shadcn/ui Skeleton is about as small as a component gets: a single div with the classes `animate-pulse rounded-md bg-accent`. You size it with your own classes, for example a 20px by 100px rounded-full bar, and compose cards out of several of them.
Because the code is copied into your project rather than installed as a dependency, you can change the animation in one line. Many teams swap the pulse for a shimmer here.
Our Form Skeleton below is a pure-CSS version of a form placeholder that breathes on the same curve as Tailwind's animate-pulse, so it sits comfortably next to shadcn components.
Watch out for: Tailwind's animate-pulse fades opacity forever. Wrap it in motion-safe or add a reduced-motion override so people who ask for less motion get a still block.
10. react-loading-skeleton: Best for skeletons that size themselves from existing styles
Platform: React, npm package (MIT)
The pitch is in the README: create skeleton screens that automatically adapt to your app. Skeleton elements take their dimensions from your existing styles, so a component can render its own loading state without a separate hand-built placeholder.
The props are short and practical: `count` for the number of lines (3.5 renders a half-width last line), `baseColor` and `highlightColor` for the shimmer, `duration` (1.5 seconds by default) and `enableAnimation`. A SkeletonTheme wrapper shares colours across a subtree.
Our Article Skeleton below is a pure-CSS alternative if you want the same text-block look without a dependency: a headline, byline and paragraph drawn on one empty div with mask layers.
Watch out for: The README documents enableAnimation but not a reduced-motion setting, so wire prefers-reduced-motion to enableAnimation yourself.
11. Next.js loading.js: Best for route-level skeletons in Next.js App Router apps
Platform: Next.js 13+, React Suspense
Drop a `loading.js` file into a route folder and Next.js wraps that route's page in a Suspense boundary, shows your loading UI instantly on navigation, and swaps in the content when it has streamed in. The docs suggest exactly this: "You can add any UI inside Loading, including a Skeleton."
Because the fallback is prefetched, navigation feels immediate, and shared layouts stay interactive while the new segment loads. That makes the route skeleton a framework feature rather than a component you remember to render.
Our Dashboard Skeleton below is a pure-CSS dashboard placeholder that fits a loading.js file as is: three KPI cards over a chart panel whose line draws itself.
Watch out for: If the layout reads uncached data (cookies, headers, uncached fetches), loading.js will not cover it and navigation can block. Move that fetch into the page or its own Suspense boundary.
12. Our Skeleton Loaders collection: Best for copying a layout-specific skeleton in plain CSS
Platform: Motions directory, free Ours
The Skeleton Loaders collection gathers our placeholders by layout: tables, inboxes, comment threads, pricing tables, profile cards, notification feeds, product pages and more. Each one is HTML and CSS only, with two custom properties for colour and size and a prefers-reduced-motion guard already written.
The Table Skeleton below shows why layout-specific matters: header, checkboxes, names, status pills and right-aligned amounts, with each cell brightening on a delay of row plus column so the sweep moves diagonally.
Watch out for: These are visual placeholders only. Add aria-busy on the region that is loading and aria-hidden on the placeholder shapes in your own markup.
13. Our Email Inbox Skeleton: Best for two-pane apps like mail, chat and support tools
Platform: Motions directory, free Ours
Most skeleton examples stop at a single card. Real apps often have two panes: a list on the left and a detail view on the right. Our Email Inbox Skeleton handles that case, with the selection stepping down the list and the reading pane shimmering again each time.
It works for anything list plus detail: help desks, CRMs, Slack-style chat. If you need the conversation itself, the Chat Skeleton pops message bubbles in from their tail corner.
Watch out for: The stepping selection suggests activity, which suits an initial load. For a quick refetch, use the still version so the list does not look like it is being clicked through.
How to choose a skeleton screen (or skip one)
Skeletons are a tool for a specific wait, not a default for every loading state.
Match it to the wait
Nielsen Norman Group's rule of thumb: under one second, show nothing; for full-page loads under ten seconds, a skeleton suits; for a single module such as a video or one dashboard card, a spinner is fine; past ten seconds, or for uploads and conversions, use a progress bar with an estimate (see our progress bar examples). Viget's 2017 test of 136 mobile users found the skeleton group rated the wait longest of three options, so do not assume a skeleton is free perceived speed. Add a short delay (around 300ms) before showing it, so fast loads never flash grey.
Match it to the layout
A skeleton only helps when you know the final layout. Size every block to the element it replaces and match its border radius, as the Singapore Government Design System advises, so nothing shifts when content arrives. A feed wants the Facebook-style card, a video grid wants YouTube's 16:9 block, a two-pane tool wants the inbox layout. If the layout depends on the data, a generic spinner from our loading animation examples is more honest than a guess.
Match it to your stack
On MUI, use its Skeleton and pick pulse or wave. On shadcn/ui, compose the one-div Skeleton. In Next.js, put the layout in loading.js. Anywhere else, or if you want the shimmer without a dependency, copy a layout from the Skeleton Loaders collection: plain HTML and CSS, one keyframe, reduced motion handled. For AI responses that stream in, a word-by-word placeholder like those in our AI thinking animation guide fits better than grey bars.
Ship a skeleton that matches your real layout
Take a screenshot of the screen that loads slowest. Draw the boxes over it: image, title, two lines, button. That is your skeleton, and it should take less than an hour to build in CSS.
If you would rather start from your actual component, paste its HTML or a screenshot into the creator and ask for its skeleton state. You get production CSS keyframes shaped to your layout, with the reduced-motion fallback included.
Make a skeleton loading state for this card component (HTML below). Match each block to the real element's size and border radius so nothing shifts on load. Use one left-to-right shimmer that crosses the card in about 1.5s, linear, looping. Tint the blocks from currentColor at low opacity so it works in light and dark mode. Under prefers-reduced-motion, stop the shimmer and show a static tint.
Common questions
What is a skeleton screen?
A skeleton screen is a low-fidelity placeholder in the shape of the content that is loading: grey blocks for images and bars for text. Luke Wroblewski described it in 2013 as a blank version of a page into which information is gradually loaded.
Is a skeleton screen better than a spinner?
Not always. Skeletons suit full-page or layout-heavy loads where you know the final structure. Spinners suit short waits on a single module. In Viget's 2017 study a spinner was rated faster than a skeleton of the same length, so test with your own users if perceived speed matters.
Should a skeleton screen be animated?
Usually a gentle animation helps, because a static grey block can look like permanent content. A slow pulse or a left-to-right shimmer both work. Turn the animation off under prefers-reduced-motion, as NN/g warns animated skeletons can distract and cause accessibility issues.
How do I make a skeleton loader in CSS?
Make blocks with a light background and rounded corners, then animate a linear-gradient highlight across them with background-position, or pulse their opacity. Our Skeleton Shimmer is a working example in a few lines of HTML and CSS.
How do skeleton screens work with screen readers?
The grey shapes mean nothing to a screen reader, so hide them with aria-hidden and set aria-busy="true" on the region that is loading, removing it when content arrives. For long waits, add visible status text outside the skeleton.
Sources
- Luke Wroblewski: Mobile Design Details: Avoid The Spinner (2013)
- Nielsen Norman Group: Skeleton Screens 101
- LogRocket: The past, present and future of skeleton screens
- Viget: A Bone to Pick with Skeleton Screens (2017)
- MUI Skeleton documentation
- react-loading-skeleton on GitHub
- Next.js loading.js reference
- Singapore Government Design System: Skeleton