PD06▸📊 Data Display · 資料展示▸載入
Skeleton
骨架載入
難度 ★★★★★標籤 skeleton · pulse · 載入3 min read
模式簡述
Skeleton:載入時的灰色佔位形狀。比 spinner 更精準傳達「會有什麼」的預期。
Claude Code Prompt
參考:PD06 skeleton 組件:Skeleton.tsx Skeleton:載入時的灰色佔位形狀。比 spinner 更精準傳達「會有什麼」的預期。 限制: - 形狀和實際內容不像 - pulse 動畫太快 - 永遠不消失
完整程式碼
"use client";
type State = "text" | "card" | "list";
export function Skeleton({ state = "card" }: { state?: State }) {
if (state === "text") {
return (
<div className="w-full max-w-md space-y-3">
<div className="h-6 w-2/3 animate-pulse rounded bg-muted" />
<div className="space-y-2">
<div className="h-3 w-full animate-pulse rounded bg-muted" />
<div className="h-3 w-full animate-pulse rounded bg-muted" />
<div className="h-3 w-3/4 animate-pulse rounded bg-muted" />
</div>
</div>
);
}
if (state === "list") {
return (
<ul className="w-full max-w-md space-y-3">
{[0, 1, 2, 3, 4].map((i) => (
<li key={i} className="flex items-center gap-3">
<div className="h-9 w-9 shrink-0 animate-pulse rounded-full bg-muted" />
<div className="flex-1 space-y-1.5">
<div className="h-3 w-1/2 animate-pulse rounded bg-muted" />
<div className="h-2.5 w-3/4 animate-pulse rounded bg-muted" />
</div>
</li>
))}
</ul>
);
}
return (
<div className="w-full max-w-sm overflow-hidden rounded-lg border border-border bg-background/30">
<div className="aspect-[16/9] w-full animate-pulse bg-muted" />
<div className="space-y-2 p-3">
<div className="h-4 w-2/3 animate-pulse rounded bg-muted" />
<div className="h-3 w-full animate-pulse rounded bg-muted" />
<div className="h-3 w-1/2 animate-pulse rounded bg-muted" />
</div>
</div>
);
}何時用
- ✅ 首屏資料載入
- ✅ API 還在 fetch 時的過渡
- ✅ 圖片載入前的位置佔位
何時別用
- ❌ 極短的等待(< 200ms 不需要,spinner 也行)
- ❌ 資料量不可預期時(用 spinner)
- ❌ 錯誤時(不要假裝在 loading)
我踩過的坑
-
形狀和實際內容不像:使用者預期看到頭像,loading 出來是長條。要對應實際 layout。
-
pulse 動畫太快:每秒 1 次 OK,每秒 3 次反而焦慮。
-
永遠不消失:API fail 後還是在 pulse,使用者以為當機。記得 fail 後切到 error state。
相關模式
- PD05
- PD07