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.
Overview
The Skeleton component in HeroUI v3 has been simplified to a standalone placeholder component. The isLoaded prop has been removed, requiring manual control of skeleton visibility.
Structure Changes
v2: Wrapper Component
In v2, Skeleton wrapped children and showed/hid them based on isLoaded:
import { Skeleton } from "@heroui/react";
<Skeleton isLoaded={isLoaded}>
<div className="h-24 bg-secondary" />
</Skeleton>v3: Standalone Component
In v3, Skeleton is a standalone placeholder that you control visibility of manually:
import { Skeleton } from "@heroui/react";
{!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 Prop | Notes |
|---|---|---|
isLoaded | - | Removed - control visibility manually |
disableAnimation | animationType | Changed to "shimmer" | "pulse" | "none" |
classNames | - | Use className prop directly |
children | - | No longer wraps children |
3. Removed Props
The following props are no longer available in v3:
isLoaded- Control skeleton visibility manually with conditional renderingclassNames- UseclassNameprop directlychildren- Skeleton no longer wraps content
4. New Props
animationType- Controls animation type:"shimmer"(default),"pulse", or"none"
Migration Examples
Basic Usage
import { Skeleton } from "@heroui/react";
export default function App() {
return (
<Skeleton className="rounded-lg">
<div className="h-24 rounded-lg bg-default-300" />
</Skeleton>
);
}import { Skeleton } from "@heroui/react";
export default function App() {
return <Skeleton className="h-24 rounded-lg" />;
}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 Control
<Skeleton disableAnimation>
<div className="h-24 bg-default-300" />
</Skeleton><Skeleton animationType="none" className="h-24 rounded-lg" />Different 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" />Custom Styling
<Skeleton
classNames={{
base: "custom-base",
content: "custom-content"
}}
>
<div className="h-24 bg-default-300" />
</Skeleton><Skeleton className="custom-skeleton 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>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.
Breaking Changes 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
Tips for Migration
- Remove
isLoaded: Replace with conditional rendering ({!isLoaded ? <Skeleton /> : <Content />}) - Remove children: Skeleton no longer wraps content - render skeleton and content separately
- Update animation: Change
disableAnimation={true}toanimationType="none" - Update styling: Replace
classNamesprop withclassNameprop - Add dimensions: Make sure to add height/width classes since skeleton no longer inherits from children
- Use conditional rendering: Show skeleton when loading, content when loaded
Need Help?
For v3 Skeleton features and API:
- See the API Reference
- Check interactive examples
For community support: