27.5k

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 Propv3 PropNotes
isLoaded-Removed - control visibility manually
disableAnimationanimationTypeChanged 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 rendering
  • classNames - Use className prop directly
  • children - 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

  1. No Children Wrapping: Skeleton no longer wraps children - it's a standalone placeholder
  2. No isLoaded Prop: Control visibility manually with conditional rendering
  3. Animation Control: disableAnimationanimationType ("shimmer", "pulse", "none")
  4. Styling: classNamesclassName prop directly
  5. Simplified API: Much simpler component focused on being a placeholder

Tips for Migration

  1. Remove isLoaded: Replace with conditional rendering ({!isLoaded ? <Skeleton /> : <Content />})
  2. Remove children: Skeleton no longer wraps content - render skeleton and content separately
  3. Update animation: Change disableAnimation={true} to animationType="none"
  4. Update styling: Replace classNames prop with className prop
  5. Add dimensions: Make sure to add height/width classes since skeleton no longer inherits from children
  6. Use conditional rendering: Show skeleton when loading, content when loaded

Need Help?

For v3 Skeleton features and API:

For community support: