Skip to Content
UX Patterns for Devs GPT is now available! Read more →
GlossarySkeleton Screen

Skeleton Screen

A skeleton screen (also called skeleton loader or ghost element) is a version of the UI that doesn’t contain actual content but instead shows placeholder shapes that mimic the page’s layout. These placeholders are displayed while the real content is loading, giving users a sense of progress and reducing perceived loading time.

Why Use Skeleton Screens?

Benefits Over Traditional Loading

  • Reduced perceived wait time - Users feel the app is faster
  • Progressive content reveal - Content appears gradually
  • Maintained layout stability - Prevents content jumping
  • Better than spinners - Shows what’s coming, not just “loading”
  • Improved user experience - Less jarring than blank screens

Anatomy of a Skeleton Screen

/* Basic skeleton component */ .skeleton { background: linear-gradient( 90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75% ); background-size: 200% 100%; animation: loading 1.5s infinite; } @keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } /* Respect user's motion preferences */ @media (prefers-reduced-motion: reduce) { .skeleton { animation: none; background-position: 0 0; transition: none; } } /* Shape variations */ .skeleton-text { height: 16px; border-radius: 4px; margin-bottom: 8px; } .skeleton-title { height: 24px; width: 60%; } .skeleton-avatar { width: 48px; height: 48px; border-radius: 50%; } .skeleton-image { width: 100%; height: 200px; border-radius: 8px; }

Implementation Examples

Card Skeleton

<div class="card-skeleton"> <div class="skeleton skeleton-image"></div> <div class="card-body"> <div class="skeleton skeleton-title"></div> <div class="skeleton skeleton-text"></div> <div class="skeleton skeleton-text" style="width: 80%"></div> </div> </div>

List Item Skeleton

<div class="list-item-skeleton"> <div class="skeleton skeleton-avatar"></div> <div class="content"> <div class="skeleton skeleton-text" style="width: 40%"></div> <div class="skeleton skeleton-text" style="width: 60%"></div> </div> </div>

React Component

function SkeletonCard({ isLoading, children }) { if (isLoading) { return ( <div className="card"> <div className="skeleton skeleton-image" /> <div className="skeleton skeleton-title" /> <div className="skeleton skeleton-text" /> </div> ); } return children; } // Usage <SkeletonCard isLoading={!data}> {data && <Card data={data} />} </SkeletonCard>

Design Principles

Match Content Structure

The skeleton should closely resemble the actual content layout:

<!-- Skeleton matches real content structure --> <!-- Skeleton --> <div class="skeleton-post"> <div class="skeleton skeleton-avatar"></div> <div class="skeleton skeleton-text" style="width: 150px"></div> <div class="skeleton skeleton-image"></div> <div class="skeleton skeleton-text"></div> </div> <!-- Real Content --> <div class="post"> <img class="avatar" src="user.jpg" /> <h3 class="username">John Doe</h3> <img class="post-image" src="photo.jpg" /> <p class="caption">Beautiful sunset!</p> </div>

Animation Types

Shimmer Effect (Most Common)

.skeleton-shimmer { background: linear-gradient( 90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75% ); animation: shimmer 1.5s infinite; }

Pulse Effect

.skeleton-pulse { animation: pulse 1.5s ease-in-out infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }

Wave Effect

.skeleton-wave { position: relative; overflow: hidden; } .skeleton-wave::after { content: ""; position: absolute; top: 0; right: 0; bottom: 0; left: 0; transform: translateX(-100%); background: linear-gradient( 90deg, transparent, rgba(255, 255, 255, 0.4), transparent ); animation: wave 1.5s infinite; } @keyframes wave { 100% { transform: translateX(100%); } }

When to Use Skeleton Screens

Good Use Cases ✅

  • Initial page load - First meaningful paint
  • Lazy-loaded content - Images, videos, embeds
  • Dynamic content updates - Search results, filtered lists
  • Infinite scroll - New items loading
  • Dashboard widgets - Multiple data sources
  • Card-based layouts - Consistent, repeatable structures

Poor Use Cases ❌

  • Very fast loads (< 300ms) - Can cause flashing
  • Unpredictable layouts - When structure varies greatly
  • Error states - Use error messages instead
  • Small UI elements - Buttons, badges, icons
  • Critical actions - Payment forms, urgent alerts

Performance Considerations

Prevent Layout Shift

/* Define dimensions to prevent CLS */ .skeleton-container { min-height: 200px; /* Matches loaded content */ } .skeleton-image { aspect-ratio: 16 / 9; /* Maintains ratio */ }

Progressive Loading

// Load critical content first async function loadContent() { // Show skeleton showSkeleton(); // Load in priority order const critical = await fetchCriticalData(); renderCritical(critical); const secondary = await fetchSecondaryData(); renderSecondary(secondary); // Hide skeleton hideSkeleton(); }

Accessibility

Screen Reader Considerations

<!-- Announce loading state --> <div role="status" aria-live="polite" aria-label="Loading content" aria-busy="true"> <div class="skeleton-card"> <!-- Skeleton elements --> </div> </div> <!-- Hide decorative skeletons --> <div class="skeleton" aria-hidden="true"></div>

Provide Loading Context

<div class="loading-container"> <span class="visually-hidden">Loading posts...</span> <div class="skeleton-list"> <!-- Skeleton items --> </div> </div>

Common Libraries

React

  • react-loading-skeleton - Automatic skeleton generation
  • react-content-loader - SVG-based skeletons
  • react-placeholders - Ready-made placeholder components

Vue

  • vue-loading-skeleton - Vue skeleton components
  • vue-content-loader - SVG placeholder loader

CSS Frameworks

  • Bootstrap - .placeholder classes
  • Tailwind CSS - animate-pulse utility
  • Material-UI - <Skeleton> component

Best Practices

Do’s ✅

  • Match the skeleton to actual content layout
  • Use smooth, subtle animations
  • Keep skeleton colors low contrast
  • Define dimensions to prevent layout shift
  • Remove skeletons once content loads
  • Consider showing partial real content as it arrives

Don’ts ❌

  • Don’t use skeletons for very fast loads
  • Avoid overly complex skeleton layouts
  • Don’t animate too aggressively
  • Never show skeletons indefinitely
  • Don’t use for error or empty states

Alternative Loading Patterns

  • Spinners - Simple, doesn’t show structure
  • Progress bars - Shows completion percentage
  • Lazy loading - Load content as needed
  • Progressive image loading - Blur to sharp
  • Optimistic UI - Show expected result immediately

Key Takeaways

  • Skeleton screens reduce perceived loading time
  • They should match the structure of actual content
  • Use subtle animations like shimmer or pulse
  • Prevent layout shift by defining dimensions
  • Consider accessibility with proper ARIA labels
  • Not suitable for all loading scenarios

Stay updated with new patterns

Get notified when new UX patterns are added to the collection.

Last updated on