The Skeleton component is a loading placeholder that provides a visual representation of content while it's being loaded. It helps improve perceived performance by showing users the structure of content before it appears.
import { Skeleton } from '@/components/skeleton'export default function SkeletonExample() {return (<Skeleton className="h-4 w-full" />)}
Preview:
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | '' | CSS classes for styling the skeleton |
color | SkeletonColor | 'default' | Background color variant |
colorIntensity | SkeletonColorIntensity | '200' | Color intensity (100-900) |
variant | 'default' | 'circular' | 'rectangular' | 'text' | 'avatar' | 'default' | Shape variant |
size | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Size variant |
width | string | number | - | Custom width |
height | string | number | - | Custom height |
animation | 'pulse' | 'wave' | 'none' | 'pulse' | Animation type |
type SkeletonColor =| 'default'| 'gray'| 'slate'| 'zinc'| 'neutral'| 'stone'| 'red'| 'orange'| 'amber'| 'yellow'| 'lime'| 'green'| 'emerald'| 'teal'| 'cyan'| 'sky'| 'blue'| 'indigo'| 'violet'| 'purple'| 'fuchsia'| 'pink'| 'rose'| 'gradient'| 'glass';type SkeletonColorIntensity = '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
<Skeleton className="h-4 w-full" />
Preview:
<div className="space-y-2"><Skeleton className="h-2 w-full" /><Skeleton className="h-4 w-full" /><Skeleton className="h-6 w-full" /><Skeleton className="h-8 w-full" /></div>
Preview:
<Skeleton className="h-12 w-12 rounded-full" />
Preview:
<div className="space-y-2"><Skeleton className="h-4 w-[250px]" /><Skeleton className="h-4 w-[200px]" /><Skeleton className="h-4 w-[150px]" /></div>
Preview:
// Default gray<Skeleton color="default" colorIntensity="200" />// Blue skeleton<Skeleton color="blue" colorIntensity="300" />// Green skeleton<Skeleton color="green" colorIntensity="400" />
Preview:
// Gradient skeleton<Skeleton color="gradient" />// Glass effect<Skeleton color="glass" />
Preview:
Preview:
<div className="space-y-2"><Skeleton color="blue" colorIntensity="100" /><Skeleton color="blue" colorIntensity="300" /><Skeleton color="blue" colorIntensity="500" /><Skeleton color="blue" colorIntensity="700" /></div>
Preview:
import { Skeleton } from '@/components/skeleton'<div className="border rounded-lg p-4 space-y-3"><Skeleton color="blue" colorIntensity="200" className="h-4 w-3/4" /><Skeleton color="blue" colorIntensity="200" className="h-4 w-full" /><Skeleton color="blue" colorIntensity="200" className="h-4 w-5/6" /><div className="flex space-x-2"><Skeleton color="blue" colorIntensity="300" className="h-8 w-20" /><Skeleton color="blue" colorIntensity="300" className="h-8 w-16" /></div></div>
import { Skeleton } from '@/components/skeleton'<div className="flex items-center space-x-4"><Skeleton className="h-12 w-12 rounded-full" /><div className="space-y-2"><Skeleton className="h-4 w-[250px]" /><Skeleton className="h-4 w-[200px]" /></div></div>
<div className="space-y-3">{Array.from({ length: 5 }).map((_, i) => (<div key={i} className="flex items-center space-x-3"><Skeleton className="h-10 w-10 rounded-full" /><div className="space-y-2 flex-1"><Skeleton className="h-4 w-full" /><Skeleton className="h-3 w-2/3" /></div></div>))}</div>
<div className="space-y-3">{Array.from({ length: 4 }).map((_, i) => (<div key={i} className="flex space-x-4"><Skeleton className="h-4 w-20" /><Skeleton className="h-4 w-32" /><Skeleton className="h-4 w-24" /><Skeleton className="h-4 w-16" /></div>))}</div>
Make skeleton placeholders match the actual content structure.
// ✅ Good - Matches content structure<div className="space-y-3"><Skeleton className="h-6 w-3/4" /> {/* Title */}<Skeleton className="h-4 w-full" /> {/* Description */}<Skeleton className="h-4 w-2/3" /> {/* Subtitle */}<div className="flex space-x-2"><Skeleton className="h-8 w-20" /> {/* Button */}<Skeleton className="h-8 w-16" /> {/* Button */}</div></div>
Use appropriate sizing that matches the expected content.
// ✅ Good - Appropriate sizing<div className="space-y-2"><Skeleton className="h-6 w-3/4" /> {/* Large title */}<Skeleton className="h-4 w-full" /> {/* Body text */}<Skeleton className="h-3 w-1/2" /> {/* Small text */}</div>
Use consistent spacing between skeleton elements.
// ✅ Good - Consistent spacing<div className="space-y-3"><Skeleton className="h-4 w-full" /><Skeleton className="h-4 w-full" /><Skeleton className="h-4 w-full" /></div>
Use realistic proportions that match actual content.
// ✅ Good - Realistic proportions<div className="flex items-center space-x-4"><Skeleton className="h-12 w-12 rounded-full" /> {/* Avatar */}<div className="space-y-2 flex-1"><Skeleton className="h-4 w-3/4" /> {/* Name */}<Skeleton className="h-3 w-1/2" /> {/* Role */}</div></div>
Ensure smooth transitions when replacing skeletons with actual content.
// ✅ Good - Smooth transitions{loading ? (<Skeleton className="h-4 w-full" />) : (<p className="text-gray-900">Actual content</p>)}
You can customize the skeleton appearance using CSS classes:
<Skeleton className="h-4 w-full bg-gray-300 rounded-lg" />
Use different colors for different contexts:
// Light theme<Skeleton className="h-4 w-full bg-gray-200" />// Dark theme<Skeleton className="h-4 w-full bg-gray-700" />// Custom color<Skeleton className="h-4 w-full bg-blue-100" />
Use different animation styles:
// Pulse animation (default)<Skeleton className="h-4 w-full animate-pulse" />// Bounce animation<Skeleton className="h-4 w-full animate-bounce" />// No animation<Skeleton className="h-4 w-full" />
Create dynamic skeletons based on content type:
const renderSkeleton = (type: 'card' | 'list' | 'profile') => {switch (type) {case 'card':return (<div className="space-y-3"><Skeleton className="h-4 w-3/4" /><Skeleton className="h-4 w-full" /><Skeleton className="h-4 w-2/3" /></div>);case 'list':return (<div className="space-y-2">{Array.from({ length: 5 }).map((_, i) => (<Skeleton key={i} className="h-4 w-full" />))}</div>);case 'profile':return (<div className="flex items-center space-x-4"><Skeleton className="h-12 w-12 rounded-full" /><div className="space-y-2"><Skeleton className="h-4 w-[200px]" /><Skeleton className="h-3 w-[150px]" /></div></div>);default:return <Skeleton className="h-4 w-full" />;}};
Use skeletons with different loading states:
const LoadingState = ({ type }: { type: 'initial' | 'refreshing' | 'error' }) => {switch (type) {case 'initial':return <Skeleton className="h-4 w-full" />;case 'refreshing':return <Skeleton className="h-4 w-full opacity-50" />;case 'error':return <div className="h-4 w-full bg-red-100 rounded" />;default:return null;}};
- Progress: For loading progress indicators
- Spinner: For loading spinners
- Alert: For loading error states
- Card: For skeleton card layouts
- Button: For skeleton button states
For the complete API reference and advanced usage patterns, see the Skeleton API documentation.
