---
title: Skeleton
description: Skeleton 从 HeroUI v2 到 v3 的迁移指南。
---

<Callout type="info">
  完整的 API 参考、样式指南与高级示例请参阅 [v3 Skeleton 文档](/docs/react/components/skeleton)。本指南只关注从 HeroUI v2 的迁移。
</Callout>

## 结构变化

在 v2 中，Skeleton 会包裹其子节点，并根据 `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>
  );
}
```

在 v3 中，Skeleton 改为独立的占位符，由你自己手动控制其可见性：

```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" />
      )}
    </>
  );
}
```

## 主要变化

### 1. 组件行为

**v2：** 包裹子节点，并根据 `isLoaded` 显示/隐藏  
**v3：** 独立占位符——可见性由你自行控制

### 2. Prop 变更

| v2 prop | v3 位置 | 说明 |
| ------- | ------- | ---- |
| `isLoaded` | — | 通过条件渲染手动控制可见性 |
| `disableAnimation` | `animationType` | 改用 `animationType="shimmer" \| "pulse" \| "none"`（用 `"none"` 禁用动画） |
| `classNames` | — | 直接使用 `className` prop |
| `children` | — | Skeleton 不再包裹内容 |

### 3. 新增 prop

- `animationType` —— 控制动画类型：`"shimmer"`（默认）、`"pulse"` 或 `"none"`

## 迁移示例

### 配合加载状态

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

### 独立 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>

### 动画类型

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

### 复杂示例：带内容的 Card

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

### 同步的微光效果

在 v3 中，你可以创建一次性扫过所有 Skeleton 的同步微光效果。在父容器上加上 `skeleton--shimmer` 类，并将每个子 Skeleton 的 `animationType` 设为 `"none"`：

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

这种方式适用于卡片类布局——你需要的是一次统一扫过整体的微光，而不是每个 Skeleton 各自独立地播放动画。

## 全局动画配置

在 v3 中，你可以通过 CSS 变量在全局设置默认的动画类型：

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

各个组件上的 `animationType` prop 可以覆盖该默认值。

## 总结

1. **不再包裹子节点**：Skeleton 不再包裹其子节点——它本身就是一个独立的占位符
2. **不再有 `isLoaded` prop**：通过条件渲染手动控制可见性
3. **动画控制**：`disableAnimation` → `animationType`（`"shimmer"`、`"pulse"`、`"none"`）
4. **样式**：`classNames` → 直接使用 `className` prop
5. **简化的 API**：组件更简单，专注于占位符职责
6. **同步微光**：在父容器上使用 `skeleton--shimmer` 类，并将子 Skeleton 的 `animationType` 设为 `"none"`，即可实现统一的微光扫过效果
