Skip to content
Protocore Design Systemv1.6.9

/// Feedback

LoadingSkeleton

Shimmering placeholder blocks that hold a layout while its real content loads.

import { LoadingSkeleton, SkeletonText, SkeletonBlock } from "@protocore/pds";
View as Markdown

Basics

LoadingSkeleton renders a stack of shimmering bars. The last bar is shortened automatically so the group reads as a paragraph rather than a block.

Text and block presets

SkeletonBlock is a single solid shape for avatars, thumbnails and media; SkeletonText fakes lines of copy. Compose them to mirror the real layout.

Shape it like the content

A skeleton earns its keep when it matches the dimensions of what replaces it — the same card, the same block height — so nothing jumps when data arrives.

The five-state doctrine

Every data-driven surface in Protocore renders one of five states — never a blank frame. Design all five up front:

1. Loading — the shape of the content, not a spinner in the void: a LoadingSkeleton that holds the layout. 2. Empty — the request succeeded but there is legitimately nothing: EmptyState. 3. Error — the request failed: ErrorState, with a retry and a debugId. 4. Pending — a mutation is in flight over already-rendered data: a Spinner or ProgressBar layered on top, content still visible. 5. Data — the happy path.

This is the *console pattern*: a panel is a small state machine, and a skeleton is what the machine shows on entry. Skipping straight from nothing to data is the drift this doctrine exists to prevent.

When to use it

Use a skeleton for the first load of a known layout — a table, a card grid, a detail panel. Prefer it over a bare Spinner whenever you can predict the shape of the result, because it reduces layout shift and perceived latency.

For a mutation over content that is *already* on screen, don't swap it for a skeleton — keep the data and layer a pending affordance instead.

Usage

Do

  • Match the skeleton's size and count to the real content it stands in for.
  • Use it for first loads of predictable, structured layouts.
  • Compose SkeletonBlock + SkeletonText to mirror the true shape.
  • Swap to the real content — or an EmptyState / ErrorState — as soon as the request resolves.

Don't

  • Show a skeleton whose shape doesn't match what replaces it.
  • Keep skeletons up indefinitely — cap the wait, then fall through to an ErrorState.
  • Skeleton content that is already rendered during a background refresh.
  • Add your own `aria-live` — the skeleton is deliberately `aria-hidden`.

Accessibility

  • Skeletons are decorative: the root carries `aria-hidden="true"`, so nothing is announced.
  • Announce the wait elsewhere — a `role="status"` region, a Spinner's label, or an aria-busy container around the region.
  • The shimmer respects `prefers-reduced-motion`.
  • Because it is hidden from assistive tech, never convey information (like a count) through skeleton bars alone.

Mobile (React Native)

Preview. @protocore/pds-mobile ships the React Native sibling of LoadingSkeleton. It mirrors the web API where React Native allows; the package is a preview with no device-level QA yet, so pin it and expect small changes.

Import it from the mobile package (not @protocore/pds), inside a <PdsProvider> — there is no stylesheet, so style (a ViewStyle) replaces className and every value comes from the theme:

import { LoadingSkeleton, SkeletonText, SkeletonBlock } from "@protocore/pds-mobile";

Parity with web. Shimmering placeholder blocks, plus the SkeletonText / SkeletonBlock presets.

  • Web animates a moving linear-gradient; RN has no CSS gradients, so the shimmer is an Animated opacity pulse over a fill-ghost block.
  • lines, height, and width match the web API.
<LoadingSkeleton lines={3} />

LoadingSkeleton props

PropTypeDefaultDescription
classNamestring
heightstring | number96Height of each bar (number = px). Defaults to a text line.
linesnumber3How many stacked placeholder bars to render.
styleCSSProperties
widthstring | numberWidth of each bar (number = px). Defaults to full width.

SkeletonText props

PropTypeDefaultDescription
classNamestring
linesnumber3Number of text lines to fake.
styleCSSProperties
widthstring | numberWidth of the final (short) line.

SkeletonBlock props

PropTypeDefaultDescription
classNamestring
heightstring | number96Block height (number = px).
styleCSSProperties
widthstring | numberBlock width (number = px).

Related

  • SpinnerA minimal 1em border spinner that announces an indeterminate loading status inline.
  • EmptyStateA centered, dashed-frame placeholder for a surface that legitimately has nothing to show.
  • ErrorStateA centered failure panel with a danger accent rule, optional debugId tag and a retry affordance.