Feedback/Skeleton

Skeleton

Use to show a placeholder while content is loading.

Themed

When to Use

Use Skeleton when you need to:

  • Show a placeholder while content is loading asynchronously
  • Preserve the layout structure during data fetching to prevent content shift
  • Reduce perceived loading time by hinting at the shape of incoming content
  • Build progressive UIs that reveal content incrementally
  • Replace spinners when the content structure is known in advance

When Not to Use

  • Unknown content shape or indeterminate loading - use Spinner
  • Short loading times under 300ms - prefer no indicator to avoid flicker
  • Background processes or action confirmations - use Progress or Spinner
  • Empty state with no content to load - use an empty-state illustration instead

Default

Profile skeleton with avatar circle and two text lines.

Card

Card skeleton with image placeholder and text lines for titles or descriptions.

List

Repeating list items with avatar and two-line text placeholders.

UX & Design Guidelines

Content Fidelity

Match skeleton dimensions as closely as possible to the actual content they replace. Use the same h-* and w-* values as the final rendered elements. This ensures a smooth, shift-free transition when content loads.

Spacing & Layout

Group related skeletons with consistent spacing using space-y-2 for tight text lines and space-y-4 for distinct content blocks. Use flex and space-x-4 for horizontal layouts like avatar + text combinations.

Animation & Timing

The default animate-pulse animation provides a subtle breathing effect. Avoid showing skeletons for loads under 300ms to prevent flicker. For very long loads (over 5 seconds), consider adding a text status message alongside the skeleton.

Color & Contrast

Skeletons use bg-accent which adapts automatically to light and dark modes. The subtle contrast against bg-background communicates a placeholder without drawing excessive attention. Avoid overriding the background color unless matching a specific container.

Accessibility

Keyboard Navigation

  • Tab -- Skeleton is not focusable; focus moves through interactive elements as normal
  • Escape -- No action; skeleton is decorative and non-interactive
  • Once content loads, standard keyboard navigation applies to the rendered elements

Screen Reader Support

  • Skeleton is purely decorative and does not require its own ARIA role
  • Wrap the loading region with aria-busy="true" while loading
  • Use a role="status" live region with aria-live="polite" to announce loading state
  • Include sr-only text like "Loading content..." for screen reader users

Focus Management

When content replaces skeleton, do not force focus unless the user initiated the load (e.g., clicking a "Load more" button). For automatic loads, let the user's current focus remain undisturbed. If the loaded content contains interactive elements, ensure they appear in logical Tab order.

Reduced Motion

The animate-pulse animation respects prefers-reduced-motion. Users with motion sensitivity will see a static placeholder instead of the pulsing animation. No additional configuration is required.

ARIA Attributes

{/* Wrap loading area with aria-busy */}
<div aria-busy="true" aria-label="Loading profile">
  <Skeleton className="h-12 w-12 rounded-full" />
  <Skeleton className="h-4 w-[200px]" />
</div>

{/* Announce loading state with live region */}
<div role="status" aria-live="polite">
  <span className="sr-only">Loading content...</span>
  <Skeleton className="h-4 w-full" />
</div>

{/* Replace skeleton when content loads */}
{isLoading ? (
  <div aria-busy="true">
    <Skeleton className="h-4 w-[200px]" />
  </div>
) : (
  <p>{content}</p>
)}

API Reference

The Skeleton component accepts the following props in addition to standard div attributes.

PropTypeDefaultDescription
classNamestring""CSS classes for sizing, shape, and custom styles. Use h-*, w-*, and rounded-* utilities.
childrenReact.ReactNodeundefinedOptional children to render inside the skeleton container.
...propsReact.ComponentProps<"div">All standard HTML div attributes are forwarded to the root element.