Skeleton
Used to show a placeholder while content is loading.
<Skeleton className="h-4 w-32" />Usage
import { Skeleton } from "@unkey/ui"
<Skeleton className="h-4 w-32" />
Examples
Card
import { Card, CardContent, CardHeader, Skeleton } from "@unkey/ui";
export function SkeletonCard() {
return (
<Card className="w-full max-w-xs">
<CardHeader className="flex-row items-center gap-4">
<Skeleton className="size-10 rounded-[10px]" />
<div className="flex min-w-0 flex-1 flex-col gap-2">
<Skeleton className="h-3.5 w-24" />
<Skeleton className="h-3 w-32" />
</div>
</CardHeader>
<CardContent className="flex flex-col gap-2">
<Skeleton className="h-5 w-40" />
<div className="flex items-center justify-between">
<Skeleton className="h-4 w-16" />
<Skeleton className="h-4 w-16" />
</div>
</CardContent>
</Card>
);
}List
import { Card, Skeleton } from "@unkey/ui";
export function SkeletonList() {
return (
<Card className="w-full max-w-2xl">
<div className="divide-y divide-border">
<div className="flex items-center gap-4 px-5 py-4">
<Skeleton className="size-8 shrink-0 rounded-full" />
<div className="flex min-w-0 flex-1 flex-col gap-2">
<Skeleton className="h-4 w-2/3" />
<Skeleton className="h-3 w-1/3" />
</div>
<Skeleton className="h-3 w-20 shrink-0" />
</div>
<div className="flex items-center gap-4 px-5 py-4">
<Skeleton className="size-8 shrink-0 rounded-full" />
<div className="flex min-w-0 flex-1 flex-col gap-2">
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-3 w-2/5" />
</div>
<Skeleton className="h-3 w-20 shrink-0" />
</div>
<div className="flex items-center gap-4 px-5 py-4">
<Skeleton className="size-8 shrink-0 rounded-full" />
<div className="flex min-w-0 flex-1 flex-col gap-2">
<Skeleton className="h-4 w-1/2" />
<Skeleton className="h-3 w-1/4" />
</div>
<Skeleton className="h-3 w-20 shrink-0" />
</div>
</div>
</Card>
);
}Best practices
Skeleton is one of three loading primitives in @unkey/ui. Use it when async data will fill a known layout, like a table or a card grid. Don’t use Skeletons on permanent UI or empty states, it misleads users about what’s coming.
For a single action use <Loading type="spinner" />, and for an indeterminate wait use <Loading type="dots" />.
Sizing
Size the skeleton to match the content it replaces. Skeleton className="h-4 w-32" for a 128px text bar prevents layout shift when the real text loads in.
Use rounded-full for circular avatars. The default rounded-sm already matches text bars, buttons, and badges in the dashboard, so leave it alone for those.
Accessibility
Set aria-busy="true" on loading regions, not skeletons. Announce completion with aria-live="polite" on the destination container. Don’t put focusable controls inside a skeleton.
Skeletons should respect prefers-reduced-motion via motion-reduce:animate-none.