Skeleton
Visual placeholder used to represent loading content while preserving layout structure.
Preserve layout while content loadsLink to section
The Skeleton component is a visual placeholder used during loading states.
It helps:
- preserve layout structure
- reduce layout shift
- improve perceived performance
- communicate that content is expected soon
- keep product surfaces stable while data loads
Skeleton is a pure UI primitive. It renders shapes and does not own any loading, fetching, or async logic.
Core idea
Skeleton shows the shape of content that is coming. It is not a message, an explanation, or an error state.
ImportLink to section
import { Skeleton, type SkeletonProps } from "@pycolors/ui";Basic usageLink to section
Use Skeleton to temporarily replace content while data is loading.
import { Skeleton } from "@pycolors/ui";
export function BasicSkeleton() {
return (
<div className="space-y-2">
<Skeleton className="h-4 w-2/3" />
<Skeleton className="h-4 w-1/2" />
</div>
);
}When to use SkeletonLink to section
Use Skeleton when the layout is known and real content is expected soon.
Known layout
Use when you already know the shape of the final content.
Short wait
Use for loading content that should appear soon.
Data surfaces
Use in cards, tables, lists, dashboards, and profile sections.
Card loading stateLink to section
Skeleton composes naturally with surfaces such as Card.
import {
Card,
CardHeader,
CardContent,
Skeleton,
} from "@pycolors/ui";
export function CardLoadingSkeleton() {
return (
<Card>
<CardHeader>
<Skeleton className="h-5 w-1/3" />
<Skeleton className="h-4 w-2/3" />
</CardHeader>
<CardContent>
<Skeleton className="h-24 w-full" />
</CardContent>
</Card>
);
}Table loading stateLink to section
Use Skeleton inside table rows when the table shape is known.
import {
Skeleton,
Table,
TableBody,
TableRow,
TableCell,
} from "@pycolors/ui";
export function TableLoadingSkeleton() {
return (
<Table>
<TableBody>
{Array.from({ length: 3 }).map((_, index) => (
<TableRow key={index}>
<TableCell>
<Skeleton className="h-4 w-32" />
</TableCell>
<TableCell>
<Skeleton className="h-4 w-24" />
</TableCell>
<TableCell>
<Skeleton className="h-4 w-16" />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
}Circular skeletonLink to section
Use circle for avatar, icon, or round media placeholders.
import { Skeleton } from "@pycolors/ui";
export function CircularSkeleton() {
return (
<div className="flex items-center gap-3">
<Skeleton circle className="h-10 w-10" />
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-3 w-24" />
</div>
</div>
);
}Usage patternsLink to section
Match the final content shapeLink to section
Skeleton should approximate the content it replaces: text lines, avatars, cards, or rows.
Preserve layout heightLink to section
Keep the loading state close to the final height to reduce layout shift.
Keep it simpleLink to section
Prefer a few clear shapes over a detailed fake version of the final UI.
Replace quickly when data is readyLink to section
Skeleton is temporary. It should disappear as soon as real content can render.
Use another state when loading endsLink to section
Empty, error, and success states need their own UI. Skeleton only represents loading.
Skeleton vs Spinner vs EmptyState vs AlertLink to section
| Situation | Use |
|---|---|
| Layout is known and content is loading | Skeleton |
| Short unknown background task | Spinner or inline loading |
| Request succeeded but no data exists | EmptyState |
| Request failed | Alert |
| Long-running operation requiring explanation | Dedicated status UI |
| Blocking app-level loading | Route-level loading UI |
Decision rule
Use Skeleton for expected content. Use EmptyState for valid absence. Use Alert for failure.
APILink to section
Props
| Prop | Type | Default | Description |
|---|---|---|---|
circle | boolean | false | Renders a circular skeleton |
className | string | — | Additional Tailwind classes |
Skeleton also accepts standard HTML div props.
React.HTMLAttributes<HTMLDivElement>TypeScript typesLink to section
export interface SkeletonProps
extends React.HTMLAttributes<HTMLDivElement> {
circle?: boolean;
}AccessibilityLink to section
Skeleton is visual only.
Guidelines:
- Skeleton should be rendered with
aria-hidden - Skeleton should never receive focus
- Skeleton should not be used as a status message
- Do not announce every placeholder to screen readers
- Provide accessible loading semantics at a higher level when needed
- Replace skeletons with real content as soon as possible
Accessibility rule
Skeletons describe visual shape for sighted users. They are not content and should not be announced as content.
Prefer / avoidLink to section
Prefer
- matching the final content shape
- simple placeholder geometry
- stable layout height
- table skeletons for table rows
- separate empty and error states
Avoid
- using Skeleton for errors
- using Skeleton for empty states
- overly detailed fake layouts
- long-running skeletons with no explanation
- layout shifts between loading and loaded states
Product copy guidelinesLink to section
Skeleton usually does not need copy.
Silent loading shape
Let the placeholder communicate that content is coming when the wait is short.
Too much loading copy
Avoid explaining every loading placeholder. Use copy only for longer or unusual waits.