Build Assets
UI Component

Skeleton Loader

Animated placeholder that mirrors the shape of loading content.

Platform: bothTags: skeleton, loading, placeholder, shimmer

Purpose

Represents content that is loading by showing a rough structural approximation. Reduces perceived wait time compared to a spinner by giving users a preview of the layout. Use when loading time is between 300ms and 3 seconds.

Anatomy

  • Shape blocks: rectangles and circles matching the content they replace
  • Shimmer animation: left-to-right gradient sweep, 1.5s loop
  • Color: var(--color-bg-secondary) base with slightly lighter shimmer highlight

States

  • Loading: shimmer animation active
  • Loaded: replaced by real content (no transition needed)
  • Error: replaced by error state

Tokens

spacing

borderRadius: 4px (text), 8px (cards), 999px (avatars/badges)

sizing

shimmerDuration: 1.5sshimmerEasing: ease-in-out

colors

base: var(--color-bg-secondary)highlight: var(--color-bg-hover)

Do

  • Mirror the real layout as closely as possible — same proportions, same structure
  • Use for content that loads in 300ms–3s — under 300ms show nothing, over 3s show a spinner with progress
  • Keep the number of skeleton shapes close to the number of real content items
  • Animate with a horizontal shimmer sweep — it signals active loading

Don't

  • Use skeletons for instant operations (< 300ms) — the flash is jarring
  • Show skeleton shapes that don't resemble the actual content
  • Use spinner AND skeleton simultaneously
  • Show skeleton indefinitely — switch to error state after timeout

Accessibility

  • aria-busy="true" on the container while loading
  • aria-label="Loading..." on the skeleton container
  • Remove animation for users with prefers-reduced-motion

Code Example

/* Shimmer animation */
@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}
.skeleton {
  background: linear-gradient(90deg, 
    var(--color-bg-secondary) 25%, 
    var(--color-bg-hover) 50%, 
    var(--color-bg-secondary) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.5s ease-in-out infinite;
}

Source Topic

ux psychology

Doherty Threshold

Why 400ms is the magic number