Skeleton Overview

The skeleton component displays animated placeholder content while data is loading. It provides users with a visual indication that content is loading and gives them a sense of the structure and layout of the upcoming content. The component includes multiple variants, customizable sizing, and respects user motion preferences.

When to Use

Use skeletons when:

  • Loading content that takes more than a few hundred milliseconds
  • Displaying placeholder content during data fetching
  • Maintaining layout structure during loading states
  • Creating a smooth transition between loading and loaded states
  • Providing visual feedback for better perceived performance

Don't use skeletons for:

  • Very fast loading content (under 200ms)
  • Simple operations where a spinner is more appropriate
  • One-time actions like form submissions
  • Loading states that need specific progress information
Examples Variants & Types

The skeleton component supports three main variants to match different content types:

<>
<dap-ds-stack direction="column">
<div>
  <dap-ds-typography variant="h4">Text Skeleton</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="text"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="75%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="50%"></dap-ds-skeleton>
  </dap-ds-stack>
</div>

<div>
  <dap-ds-typography variant="h4">Circular Skeleton</dap-ds-typography>
  <dap-ds-skeleton variant="circular"></dap-ds-skeleton>
</div>

<div>
  <dap-ds-typography variant="h4">Rectangular Skeleton</dap-ds-typography>
  <dap-ds-skeleton variant="rectangular"></dap-ds-skeleton>
</div>
</dap-ds-stack>
</>
Custom Sizing

Control skeleton dimensions with the width and height properties to match your content layout:

<>
<dap-ds-stack direction="column">
<div>
  <dap-ds-typography variant="h4">Custom Text Sizes</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="text" width="100%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="85%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="65%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="40%"></dap-ds-skeleton>
  </dap-ds-stack>
</div>

<div>
  <dap-ds-typography variant="h4">Custom Circular Sizes</dap-ds-typography>
  <dap-ds-stack direction="row" align="center">
    <dap-ds-skeleton variant="circular" width="60px" height="60px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular" width="48px" height="48px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular" width="32px" height="32px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular" width="24px" height="24px"></dap-ds-skeleton>
  </dap-ds-stack>
</div>

<div>
  <dap-ds-typography variant="h4">Custom Rectangular Sizes</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="rectangular" width="300px" height="200px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" width="250px" height="100px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" width="200px" height="50px"></dap-ds-skeleton>
  </dap-ds-stack>
</div>
</dap-ds-stack>
</>
Real-World Patterns User Profile Card Loading
<div style={{border: 'var(--dds-border-width-base) solid var(--dds-border-neutral-subtle)', padding: 'var(--dds-spacing-600)', borderRadius: 'var(--dds-radius-base)', maxWidth: '320px'}}>
<dap-ds-stack direction="row" align="center">
  <dap-ds-skeleton variant="circular" width="48px" height="48px"></dap-ds-skeleton>
  <dap-ds-stack>
    <dap-ds-skeleton variant="text" width="120px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="80px"></dap-ds-skeleton>
  </dap-ds-stack>
</dap-ds-stack>

<div style={{marginTop: 'var(--dds-spacing-400)'}}>
  <dap-ds-skeleton variant="text" width="100%"></dap-ds-skeleton>
  <dap-ds-skeleton variant="text" width="85%"></dap-ds-skeleton>
  <dap-ds-skeleton variant="text" width="60%"></dap-ds-skeleton>
</div>

<div style={{marginTop: 'var(--dds-spacing-500)', display: 'flex', gap: 'var(--dds-spacing-300)'}}>
  <dap-ds-skeleton variant="rectangular" width="80px" height="32px"></dap-ds-skeleton>
  <dap-ds-skeleton variant="rectangular" width="100px" height="32px"></dap-ds-skeleton>
</div>
</div>
Article List Loading
<div style={{border: 'var(--dds-border-width-base) solid var(--dds-border-neutral-subtle)', padding: 'var(--dds-spacing-600)', borderRadius: 'var(--dds-radius-base)'}}>
<dap-ds-stack>
  <dap-ds-typography variant="h4">Latest Articles</dap-ds-typography>
  
  <dap-ds-stack>
    <dap-ds-stack direction="row">
      <dap-ds-skeleton variant="rectangular" width="120px" height="80px"></dap-ds-skeleton>
      <dap-ds-stack style={{flex: 1}}>
        <dap-ds-skeleton variant="text" width="100%"></dap-ds-skeleton>
        <dap-ds-skeleton variant="text" width="85%"></dap-ds-skeleton>
        <dap-ds-skeleton variant="text" width="60%"></dap-ds-skeleton>
        <dap-ds-stack direction="row" align="center" style={{marginTop: 'var(--dds-spacing-300)'}}>
          <dap-ds-skeleton variant="circular" width="20px" height="20px"></dap-ds-skeleton>
          <dap-ds-skeleton variant="text" width="80px"></dap-ds-skeleton>
        </dap-ds-stack>
      </dap-ds-stack>
    </dap-ds-stack>
    
    <dap-ds-stack direction="row">
      <dap-ds-skeleton variant="rectangular" width="120px" height="80px"></dap-ds-skeleton>
      <dap-ds-stack style={{flex: 1}}>
        <dap-ds-skeleton variant="text" width="95%"></dap-ds-skeleton>
        <dap-ds-skeleton variant="text" width="80%"></dap-ds-skeleton>
        <dap-ds-skeleton variant="text" width="65%"></dap-ds-skeleton>
        <dap-ds-stack direction="row" align="center" style={{marginTop: 'var(--dds-spacing-300)'}}>
          <dap-ds-skeleton variant="circular" width="20px" height="20px"></dap-ds-skeleton>
          <dap-ds-skeleton variant="text" width="80px"></dap-ds-skeleton>
        </dap-ds-stack>
      </dap-ds-stack>
    </dap-ds-stack>
    
    <dap-ds-stack direction="row">
      <dap-ds-skeleton variant="rectangular" width="120px" height="80px"></dap-ds-skeleton>
      <dap-ds-stack style={{flex: 1}}>
        <dap-ds-skeleton variant="text" width="90%"></dap-ds-skeleton>
        <dap-ds-skeleton variant="text" width="75%"></dap-ds-skeleton>
        <dap-ds-skeleton variant="text" width="55%"></dap-ds-skeleton>
        <dap-ds-stack direction="row" align="center" style={{marginTop: 'var(--dds-spacing-300)'}}>
          <dap-ds-skeleton variant="circular" width="20px" height="20px"></dap-ds-skeleton>
          <dap-ds-skeleton variant="text" width="80px"></dap-ds-skeleton>
        </dap-ds-stack>
      </dap-ds-stack>
    </dap-ds-stack>
  </dap-ds-stack>
</dap-ds-stack>
</div>
Data Table Loading
<div style={{border: 'var(--dds-border-width-base) solid var(--dds-border-neutral-subtle)', padding: 'var(--dds-spacing-600)', borderRadius: 'var(--dds-radius-base)'}}>
<dap-ds-stack>
  <dap-ds-typography variant="h4">User Management</dap-ds-typography>
  
  <div style={{display: 'grid', gridTemplateColumns: '1fr 2fr 1fr 1fr 60px', gap: 'var(--dds-spacing-400)', alignItems: 'center'}}>
    {/* Header Row */}
    <dap-ds-skeleton variant="text" width="60px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="80px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="70px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="60px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="40px"></dap-ds-skeleton>
    
    {/* Data Rows */}
    <dap-ds-skeleton variant="circular" width="32px" height="32px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="100%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="80%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="60px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" width="24px" height="24px"></dap-ds-skeleton>
    
    <dap-ds-skeleton variant="circular" width="32px" height="32px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="85%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="90%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="60px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" width="24px" height="24px"></dap-ds-skeleton>
    
    <dap-ds-skeleton variant="circular" width="32px" height="32px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="95%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="75%"></dap-ds-skeleton>
    <dap-ds-skeleton variant="text" width="60px"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" width="24px" height="24px"></dap-ds-skeleton>
  </div>
</dap-ds-stack>
</div>
Form Loading State
<div style={{border: 'var(--dds-border-width-base) solid var(--dds-border-neutral-subtle)', padding: 'var(--dds-spacing-600)', borderRadius: 'var(--dds-radius-base)', maxWidth: '400px'}}>
<dap-ds-stack>
  <dap-ds-skeleton variant="text" width="150px"></dap-ds-skeleton>
  
  <dap-ds-stack spacing="400">
    <dap-ds-stack spacing="200">
      <dap-ds-skeleton variant="text" width="100px"></dap-ds-skeleton>
      <dap-ds-skeleton variant="rectangular" width="100%" height="40px"></dap-ds-skeleton>
    </dap-ds-stack>
    
    <dap-ds-stack spacing="200">
      <dap-ds-skeleton variant="text" width="80px"></dap-ds-skeleton>
      <dap-ds-skeleton variant="rectangular" width="100%" height="40px"></dap-ds-skeleton>
    </dap-ds-stack>
    
    <dap-ds-stack spacing="200">
      <dap-ds-skeleton variant="text" width="120px"></dap-ds-skeleton>
      <dap-ds-skeleton variant="rectangular" width="100%" height="120px"></dap-ds-skeleton>
    </dap-ds-stack>
    
    <dap-ds-stack direction="row" align="center">
      <dap-ds-skeleton variant="rectangular" width="20px" height="20px"></dap-ds-skeleton>
      <dap-ds-skeleton variant="text" width="200px"></dap-ds-skeleton>
    </dap-ds-stack>
    
    <dap-ds-stack direction="row">
      <dap-ds-skeleton variant="rectangular" width="100px" height="40px"></dap-ds-skeleton>
      <dap-ds-skeleton variant="rectangular" width="80px" height="40px"></dap-ds-skeleton>
    </dap-ds-stack>
  </dap-ds-stack>
</dap-ds-stack>
</div>
Animation Types

The skeleton component supports different animation types to match your design needs:

Wave Animation (Default)

The wave animation creates a sliding shimmer effect across the skeleton:

<dap-ds-stack direction="column">
<div>
  <dap-ds-typography variant="h4">Wave Animation</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="text" animation="wave"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular" animation="wave"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" animation="wave" width="200px" height="100px"></dap-ds-skeleton>
  </dap-ds-stack>
</div>
</dap-ds-stack>
Pulse Animation

The pulse animation creates a breathing effect with opacity changes:

<dap-ds-stack direction="column">
<div>
  <dap-ds-typography variant="h4">Pulse Animation</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="text" animation="pulse"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular" animation="pulse"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" animation="pulse" width="200px" height="100px"></dap-ds-skeleton>
  </dap-ds-stack>
</div>
</dap-ds-stack>
Custom Animation

Create your own animations using the custom-keyframes attribute. This allows you to define custom CSS keyframes directly:

<dap-ds-stack direction="column">
<div>
  <dap-ds-typography variant="h4">Custom Animations</dap-ds-typography>
  <dap-ds-stack direction="column">
    <div>
      <dap-ds-typography variant="body">Bounce Animation</dap-ds-typography>
      <dap-ds-skeleton 
        variant="text" 
        animation="custom"
        custom-keyframes="0%, 20%, 50%, 80%, 100% { transform: translateY(0); } 40% { transform: translateY(-3px); } 60% { transform: translateY(-1px); }">
      </dap-ds-skeleton>
    </div>
    
    <div>
      <dap-ds-typography variant="body">Scale Animation</dap-ds-typography>
      <dap-ds-skeleton 
        variant="circular" 
        animation="custom"
        custom-keyframes="0% { transform: scale(0.5); } 50% { transform: scale(1.05); } 100% { transform: scale(0.5); }">
      </dap-ds-skeleton>
    </div>
  </dap-ds-stack>
</div>
</dap-ds-stack>
Animation Comparison

Compare different animation types side by side:

<div style={{display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 'var(--dds-spacing-600)', alignItems: 'start'}}>
<dap-ds-stack direction="column" align="center">
  <dap-ds-typography variant="h4">Wave</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="text" animation="wave"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular" animation="wave"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" animation="wave" width="120px" height="80px"></dap-ds-skeleton>
  </dap-ds-stack>
</dap-ds-stack>

<dap-ds-stack direction="column" align="center">
  <dap-ds-typography variant="h4">Pulse</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="text" animation="pulse"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular" animation="pulse"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular" animation="pulse" width="120px" height="80px"></dap-ds-skeleton>
  </dap-ds-stack>
</dap-ds-stack>

<dap-ds-stack direction="column" align="center">
  <dap-ds-typography variant="h4">Custom</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton variant="text"
      style={{
        '--dds-skeleton-color': 'red',
      }}
      animation="custom" custom-keyframes="0% { transform: skew(-50deg); } 50% { transform: skew(2deg); } 100% { transform: skew(-50deg); }"></dap-ds-skeleton>
    <dap-ds-skeleton variant="circular"
      animation="custom" custom-keyframes="0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); }"></dap-ds-skeleton>
    <dap-ds-skeleton variant="rectangular"
      style={{
        '--dds-skeleton-color': 'red',
      }}
      animation="custom" custom-keyframes="0% { border-radius: 4px; } 50% { border-radius: 20px; } 100% { border-radius: 4px; }" width="120px" height="80px">
    </dap-ds-skeleton>
  </dap-ds-stack>
</dap-ds-stack>
</div>
Custom Styling

The skeleton component supports extensive customization through CSS custom properties and parts.

Quick Customization with CSS Custom Properties

For simple customizations, use CSS custom properties directly on the component:

<dap-ds-stack direction="column">
<div>
  <dap-ds-typography variant="h4">Custom Colors</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton 
      variant="text" 
      style={{
        '--dds-skeleton-color': 'linear-gradient(90deg, transparent, rgb(59 130 246 / 20%), transparent)',
      }}>
    </dap-ds-skeleton>
    <dap-ds-skeleton 
      variant="text" 
      width="75%"
      style={{
        '--dds-skeleton-color': 'linear-gradient(90deg, transparent, rgb(34 197 94 / 20%), transparent)',
      }}>
    </dap-ds-skeleton>
  </dap-ds-stack>
</div>

<div>
  <dap-ds-typography variant="h4">Custom Animation Speed</dap-ds-typography>
  <dap-ds-stack direction="column">
    <dap-ds-skeleton 
      variant="text" 
      style={{
        '--dds-skeleton-animation-duration': '0.8s',
      }}>
    </dap-ds-skeleton>
    <dap-ds-skeleton 
      variant="text" 
      width="75%"
      style={{
        '--dds-skeleton-animation-duration': '2.5s',
      }}>
    </dap-ds-skeleton>
  </dap-ds-stack>
</div>
</dap-ds-stack>
Advanced Styling with CSS Parts and Custom Styles

Experiment with custom skeleton styling using CSS parts and the custom-styles attribute. The skeleton component exposes the base CSS part for advanced styling. Try the presets below or create your own styles:

Select a presetGradient BorderGlowing EffectCustom Animation SpeedRounded with ShadowCustom Colors
Copy CSSFormat CSSResetLightDark
CSS Editor
Live Preview
Using Custom Styles Attribute

You can also use the custom-styles attribute to inject custom CSS directly into the component's shadow DOM:

<dap-ds-stack direction="column">
<div>
  <dap-ds-typography variant="h4">Custom Styles with Border</dap-ds-typography>
  <dap-ds-skeleton
    variant="rectangular"
    width="250px"
    height="100px"
    custom-styles="
      .skeleton {
        border: 2px solid var(--dds-brand-600);
        border-radius: var(--dds-radius-large);
        box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
      }
    ">
  </dap-ds-skeleton>
</div>

<div>
  <dap-ds-typography variant="h4">Custom Styles with Gradient</dap-ds-typography>
  <dap-ds-skeleton
    variant="rectangular"
    width="250px"
    height="100px"
    custom-styles="
      .skeleton {
        background: linear-gradient(135deg, var(--dds-brand-100), var(--dds-brand-200));
        border-radius: var(--dds-radius-xlarge);
      }
    ">
  </dap-ds-skeleton>
</div>

<div>
  <dap-ds-typography variant="h4">Custom Animation with Custom Styles</dap-ds-typography>
  <dap-ds-skeleton
    variant="circular"
    width="80px"
    height="80px"
    animation="custom"
    custom-keyframes="0% { transform: rotate(0deg) scale(1); } 50% { transform: rotate(180deg) scale(1.1); } 100% { transform: rotate(360deg) scale(1); }"
    custom-styles="
      .skeleton {
        border: 3px solid var(--dds-brand-400);
        box-shadow: 0 0 12px var(--dds-brand-300);
      }
    ">
  </dap-ds-skeleton>
</div>
</dap-ds-stack>
Best Practices Performance Considerations
  • Use skeletons for content that takes longer than 200-300ms to load
  • Prefer CSS animations over JavaScript for better performance
  • Avoid too many animated skeletons on a single page
Accessibility Guidelines
  • Skeletons automatically include role="status" and aria-label="Loading..."
  • The component respects prefers-reduced-motion settings
  • Ensure skeletons are replaced with actual content in a timely manner
  • Provide alternative loading indicators for screen readers when needed
Design Guidelines
  • Match skeleton shapes and sizes to the actual content layout
  • Keep animation subtle and not distracting
  • Use consistent skeleton patterns across your application
  • Consider the user's context when choosing between animated and static skeletons
Content Matching
  • Text skeletons should approximate actual text line heights
  • Circular skeletons work well for avatars and profile pictures
  • Rectangular skeletons are ideal for images, cards, and buttons
  • Use width variations to create realistic text patterns
Importing
import { DapDSSkeleton } from 'dap-design-system'
Importing React
import { DapDSSkeletonReact } from 'dap-design-system/react'
Tree-Shakeable Imports

For optimal bundle sizes, use the tree-shakeable import syntax:

import { DapDSSkeleton } from 'dap-design-system/components'
Attributes
PropertyTypeDefaultDescription
variant"text", "circular" , "rectangular"'text'The variant of the skeleton.
widthstring, undefinedThe width of the skeleton. Can be any valid CSS width value.
heightstring, undefinedThe height of the skeleton. Can be any valid CSS height value.
noAnimationbooleanfalseWhether to animate the skeleton.
animation"wave", "pulse" , "custom"'wave'The animation type for the skeleton.
customKeyframesstring, undefinedCustom keyframes for the animation when animation="custom".
Should be a valid CSS keyframes string without the
Slots

No slots available.

Events

No custom events available.

CSS Parts
Part NameDescription
baseThe main skeleton container.
How to Use CSS Parts

You can style CSS parts using the ::part() pseudo-element selector:

/* Target a specific part */
.my-custom-dap-ds-skeleton::part(base) {
  /* Your custom styles */
}

Example usage:

<dap-ds-skeleton class="my-custom-dap-ds-skeleton">
  Skeleton
</dap-ds-skeleton>
.my-custom-dap-ds-skeleton::part(base) {
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

CSS parts allow you to style internal elements of the component while maintaining encapsulation. Learn more in our styling guide.

CSS Custom Properties
Property NameDescription
--dds-skeleton-colorThe base color of the skeleton (default: linear-gradient(90deg, transparent, rgb(0 0 0 / 10%), transparent))
--dds-skeleton-animation-durationDuration of the loading animation (default: 1.5s)
--dds-skeleton-border-radiusBorder radius for rectangular skeletons (default: var(--dds-radius-small))
--dds-skeleton-text-spacingSpacing between text lines in text variant (default: var(--dds-spacing-100))
--dds-skeleton-animation-timing-functionTiming function for the loading animation (default: ease-in-out)
How to Use CSS Custom Properties

CSS custom properties (CSS variables) can be set directly on the component or in your stylesheet:

Method 1: Inline styles (Quick customization)

<dap-ds-skeleton
  style="--dds-skeleton-color: value; --dds-skeleton-animation-duration: value;">
  Skeleton
</dap-ds-skeleton>

Method 2: CSS classes (Reusable styles)

.my-custom-dap-ds-skeleton {
  --dds-skeleton-color: value;
  --dds-skeleton-animation-duration: value;
  --dds-skeleton-border-radius: value;
}
<dap-ds-skeleton class="my-custom-dap-ds-skeleton">
  Skeleton
</dap-ds-skeleton>

Method 3: Global theme customization

/* Apply to all instances */
dap-ds-skeleton {
  --dds-skeleton-color: value;
  --dds-skeleton-animation-duration: value;
}

CSS custom properties inherit through the Shadow DOM, making them perfect for theming. Changes apply immediately without rebuilding.