Unkey / Design

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.