What is Skeleton screen?
Skeleton screen: A skeleton screen is a placeholder layout of gray shapes that mirrors a page's structure while its content loads, instead of a blank screen or spinner.
A skeleton screen renders the shape of the page before the data arrives: gray bars where text will be, rectangles where images and charts will be, rows where a table will fill in. A slow shimmer often sweeps across to show that something is happening. Because the layout is already in place, the real content drops into position without jumping, and the wait feels shorter than staring at a single spinner in an empty page.
When to use it
- Initial page or section loads where you know the layout: dashboards, feeds, tables, profile pages.
- Loads longer than about 300ms. For faster responses a skeleton flashes on and off, which is worse than nothing. Delay it slightly or skip it.
- Use a spinner for actions with unknown output, like submitting a form, and a progress bar when you can measure progress.
How to build one in Next.js
With the App Router, a loading.tsx file becomes a Suspense fallback for its route segment, streamed immediately while the page's data loads:
import { Skeleton, SkeletonCard } from "@/components/ui/skeleton"
export default function Loading() {
return (
<div aria-busy="true" className="grid gap-4 md:grid-cols-3">
<p role="status" className="sr-only">Loading dashboard</p>
<SkeletonCard />
<SkeletonCard />
<SkeletonCard />
<Skeleton className="h-64 md:col-span-3" />
</div>
)
}
Size each placeholder to match the real content. If the skeleton card is 180px tall and the real card is 240px, the page shifts when data arrives and you have traded a spinner for layout shift, which hurts both users and Cumulative Layout Shift scores.
Accessibility
- Hide the placeholder shapes from assistive technology with
aria-hidden; a list of empty boxes means nothing when read aloud. - Announce loading once with a visually hidden
role="status"message, and setaria-busy="true"on the region being filled. - Stop or slow the shimmer under prefers-reduced-motion. A static gray block still communicates loading.
- Keep skeleton colors subtle but distinct from the background in both light and dark themes.
Common mistakes
- Skeletons that never resolve because an error was not handled. Replace them with an error state after a failure.
- Showing a skeleton when the result is actually empty. Once data arrives with zero items, switch to an empty state.
- A skeleton for data already in the client cache. Render the cached data and refresh in the background.
MiniDev's Skeleton is a sunken plate with a hairline sheen, and LoadingTable gives data tables matching placeholder rows. Both are in the feedback category.
SkeletonuiTailwind skeleton loader with a sunken plate and a soft sheen that sweeps every 1.6s, plus a SkeletonCard preset with title, text lines and a media block.npx shadcn@latest add ui.minidev.pro/r/skeleton.jsonLoading TableuiA skeleton placeholder for data tables with configurable rows and columns, a header row and aria-busy so assistive tech knows content is loading.npx shadcn@latest add ui.minidev.pro/r/loading-table.json