---
title: Skeleton
description: Migration guide for Skeleton from HeroUI v2 to v3
---

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

<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
    {/* Shimmer (default) */}
    <Skeleton>
      <div className="h-24 bg-default-300" />
    </Skeleton>

    {/* No animation */}
    <Skeleton disableAnimation>
      <div className="h-24 bg-default-300" />
    </Skeleton>
    ```
  </Tab>
  <Tab value="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" />
    ```
  </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
{/* 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:

```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**: `disableAnimation` → `animationType` (`"shimmer"`, `"pulse"`, `"none"`)
4. **Styling**: `classNames` → `className` 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
