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
v2
```tsx
import { useState } from "react";
const [isLoaded, setIsLoaded] = useState(false);
<Skeleton className="rounded-lg" isLoaded={isLoaded}>
<div className="h-24 rounded-lg bg-secondary" />
</Skeleton>
```
v3
```tsx
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
v2
```tsx
<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>
```
v3
```tsx
<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
v2
```tsx
{/* Shimmer (default) */}
<Skeleton>
<div className="h-24 bg-default-300" />
</Skeleton>
{/* No animation */}
<Skeleton disableAnimation>
<div className="h-24 bg-default-300" />
</Skeleton>
```
v3
```tsx
{/* 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
v2
```tsx
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>
```
v3
```tsx
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
Local source: vendor/en/react/migration/(components)/skeleton.mdx
Generated from the pinned HeroUI v3 commit with internal links and previews adapted for JGuang.