UIUpdated April 28, 2026

Skeleton

Visual placeholder used to represent loading content while preserving layout structure.

UISkeleton

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

skeleton-import.tsx
import { Skeleton, type SkeletonProps } from "@pycolors/ui";

Basic usageLink to section

Use Skeleton to temporarily replace content while data is loading.

basic-skeleton.tsx
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.

card-loading-skeleton.tsx
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.

table-loading-skeleton.tsx
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.

circular-skeleton.tsx
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

SituationUse
Layout is known and content is loadingSkeleton
Short unknown background taskSpinner or inline loading
Request succeeded but no data existsEmptyState
Request failedAlert
Long-running operation requiring explanationDedicated status UI
Blocking app-level loadingRoute-level loading UI

Decision rule

Use Skeleton for expected content. Use EmptyState for valid absence. Use Alert for failure.

APILink to section

Props

PropTypeDefaultDescription
circlebooleanfalseRenders a circular skeleton
classNamestringAdditional Tailwind classes

Skeleton also accepts standard HTML div props.

skeleton-props.ts
React.HTMLAttributes<HTMLDivElement>

TypeScript typesLink to section

skeleton-types.ts
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.

Common questionsLink to section