Skeleton
Migration guide for Skeleton from HeroUI v2 to v3
Refer to the v3 Skeleton documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
Structure Changes
In v2, Skeleton wrapped children and showed/hid them based on isLoaded:
import { Skeleton } from "@heroui/react";
export default function App() {
const isLoaded = false;
return (
<Skeleton isLoaded={isLoaded}>
<div className="h-24 bg-secondary" />
</Skeleton>
);
}In v3, Skeleton is a standalone placeholder that you control visibility of manually:
import { Skeleton } from "@heroui/react";
export default function App() {
const isLoaded = false;
return (
<>
{!isLoaded ? (
<Skeleton className="h-24 rounded-lg" />
) : (
<div className="h-24 bg-secondary" />
)}
</>
);
}Key Changes
1. Component Behavior
v2: Wrapped children and showed/hid them based on isLoaded
v3: Standalone placeholder - you control visibility manually
2. Prop Changes
| v2 Prop | v3 Location | Notes |
|---|---|---|
isLoaded | — | Control visibility manually with conditional rendering |
disableAnimation | animationType | Use animationType="shimmer" | "pulse" | "none" (use "none" to disable) |
classNames | — | Use className prop directly |
children | — | Skeleton no longer wraps content |
3. New Props
animationType- Controls animation type:"shimmer"(default),"pulse", or"none"
Migration Examples
With Loaded State
import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
<Skeleton className="rounded-lg" isLoaded={isLoaded}>
<div className="h-24 rounded-lg bg-secondary" />
</Skeleton>import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
{!isLoaded ? (
<Skeleton className="h-24 rounded-lg" />
) : (
<div className="h-24 rounded-lg bg-secondary" />
)}Standalone Skeleton
<div className="flex items-center gap-3">
<Skeleton className="flex rounded-full w-12 h-12" />
<div className="w-full flex flex-col gap-2">
<Skeleton className="h-3 w-3/5 rounded-lg" />
<Skeleton className="h-3 w-4/5 rounded-lg" />
</div>
</div><div className="flex items-center gap-3">
<Skeleton className="h-12 w-12 shrink-0 rounded-lg" />
<div className="flex-1 space-y-2">
<Skeleton className="h-3 w-full rounded" />
<Skeleton className="h-3 w-4/5 rounded" />
</div>
</div>Animation Types
{/* Shimmer (default) */}
<Skeleton>
<div className="h-24 bg-default-300" />
</Skeleton>
{/* No animation */}
<Skeleton disableAnimation>
<div className="h-24 bg-default-300" />
</Skeleton>{/* Shimmer (default) */}
<Skeleton animationType="shimmer" className="h-24 rounded-lg" />
{/* Pulse */}
<Skeleton animationType="pulse" className="h-24 rounded-lg" />
{/* No animation */}
<Skeleton animationType="none" className="h-24 rounded-lg" />Complex Example: Card with Content
import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
<Card className="w-[200px] space-y-5 p-4" radius="lg">
<Skeleton className="rounded-lg" isLoaded={isLoaded}>
<div className="h-24 rounded-lg bg-secondary" />
</Skeleton>
<div className="space-y-3">
<Skeleton className="w-3/5 rounded-lg" isLoaded={isLoaded}>
<div className="h-3 w-full rounded-lg bg-secondary" />
</Skeleton>
<Skeleton className="w-4/5 rounded-lg" isLoaded={isLoaded}>
<div className="h-3 w-full rounded-lg bg-secondary-300" />
</Skeleton>
</div>
</Card>import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
<Card className="w-[250px] space-y-5 rounded-lg p-4">
{!isLoaded ? (
<>
<Skeleton className="h-32 rounded-lg" />
<div className="space-y-3">
<Skeleton className="h-3 w-3/5 rounded-lg" />
<Skeleton className="h-3 w-4/5 rounded-lg" />
</div>
</>
) : (
<>
<div className="h-32 rounded-lg bg-secondary" />
<div className="space-y-3">
<div className="h-3 w-3/5 rounded-lg bg-secondary" />
<div className="h-3 w-4/5 rounded-lg bg-secondary-300" />
</div>
</>
)}
</Card>Synchronized Shimmer Effect
In v3, you can create a synchronized shimmer that passes over all skeleton elements at once. Apply the skeleton--shimmer class to a parent container and set animationType="none" on each child Skeleton:
{/* The parent container drives a single shimmer across all children */}
<div className="skeleton--shimmer space-y-4 rounded-lg p-4">
<Skeleton animationType="none" className="h-32 rounded-lg" />
<div className="space-y-3">
<Skeleton animationType="none" className="h-3 w-3/5 rounded-lg" />
<Skeleton animationType="none" className="h-3 w-4/5 rounded-lg" />
</div>
</div>This is useful for card-like layouts where you want a single, unified shimmer sweep instead of each skeleton animating independently.
Global Animation Configuration
In v3, you can set a default animation type globally using CSS variables:
:root {
--skeleton-animation: pulse; /* shimmer, pulse, or none */
}This can be overridden by the animationType prop on individual components.
Summary
- No Children Wrapping: Skeleton no longer wraps children - it's a standalone placeholder
- No
isLoadedProp: Control visibility manually with conditional rendering - Animation Control:
disableAnimation→animationType("shimmer","pulse","none") - Styling:
classNames→classNameprop directly - Simplified API: Much simpler component focused on being a placeholder
- Synchronized Shimmer: Use
skeleton--shimmerclass on a parent container withanimationType="none"on children for a unified shimmer sweep