Back to Heroui

Skeleton

apps/docs/content/docs/react/migration/(components)/skeleton.mdx

3.0.56.4 KB
Original Source
<Callout type="info"> Refer to the [v3 Skeleton documentation](/docs/react/components/skeleton) for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2. </Callout>

Structure Changes

In v2, Skeleton wrapped children and showed/hid them based on isLoaded:

tsx
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:

tsx
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 Propv3 LocationNotes
isLoadedControl visibility manually with conditional rendering
disableAnimationanimationTypeUse animationType="shimmer" | "pulse" | "none" (use "none" to disable)
classNamesUse className prop directly
childrenSkeleton no longer wraps content

3. New Props

  • animationType - Controls animation type: "shimmer" (default), "pulse", or "none"

Migration Examples

With Loaded State

<Tabs items={["v2", "v3"]}> <Tab value="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>
```
</Tab> <Tab value="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" />
)}
```
</Tab> </Tabs>

Standalone Skeleton

<Tabs items={["v2", "v3"]}> <Tab value="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> </Tab> <Tab value="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> </Tab> </Tabs>

Animation Types

<Tabs items={["v2", "v3"]}> <Tab value="v2"> tsx <Skeleton> <div className="h-24 bg-default-300" /> </Skeleton> <Skeleton disableAnimation> <div className="h-24 bg-default-300" /> </Skeleton> </Tab> <Tab value="v3"> tsx <Skeleton animationType="shimmer" className="h-24 rounded-lg" /> <Skeleton animationType="pulse" className="h-24 rounded-lg" /> <Skeleton animationType="none" className="h-24 rounded-lg" /> </Tab> </Tabs>

Complex Example: Card with Content

<Tabs items={["v2", "v3"]}> <Tab value="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>
```
</Tab> <Tab value="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>
```
</Tab> </Tabs>

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:

tsx
<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:

css
:root {
  --skeleton-animation: pulse; /* shimmer, pulse, or none */
}

This can be overridden by the animationType prop on individual components.

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
  6. Synchronized Shimmer: Use skeleton--shimmer class on a parent container with animationType="none" on children for a unified shimmer sweep