ComponentsFeedback

Skeleton

The page's frame at its real sizes, with grey shapes where the data will go. Every route shows one while it loads, so a click answers at once.

Playground

Change the controls to try each prop. The code under the preview updates as you go and lists only the props you changed; copy it into your page.

import { Skeleton } from '@/ui/skeleton';<Skeleton />

Default

Lines in a SkeletonGroup. The group is the loading region screen readers hear; it stays invisible for 300ms, then fades in, and the shapes pulse gently.

import { Skeleton, SkeletonGroup } from '@/ui/skeleton';

export default function SkeletonDefault() {
  return (
    <SkeletonGroup className="flex w-full max-w-[320px] flex-col gap-100">
      <Skeleton />
      <Skeleton width="80%" />
    </SkeletonGroup>
  );
}

Shapes

Three shapes, all taken from the table: a line for text, a pill for a status, and an avatar circle for a person or a company.

import { Skeleton, SkeletonGroup } from '@/ui/skeleton';

export default function SkeletonShapes() {
  return (
    <SkeletonGroup className="flex items-center gap-300">
      <Skeleton width={120} />
      <Skeleton shape="pill" />
      <Skeleton shape="avatar" />
    </SkeletonGroup>
  );
}

Table rows

A list while it loads: the header and rows at the table's real heights (40 and 36), the avatar in the name column and the pill in the status column.

import { Skeleton, SkeletonGroup } from '@/ui/skeleton';

const rows = [
  { name: 120, email: 132, status: 68 },
  { name: 96, email: 108, status: 56 },
  { name: 140, email: 150, status: 68 },
];

export default function SkeletonTableRows() {
  return (
    <SkeletonGroup label="Loading candidates" className="w-full overflow-hidden rounded-xlarge border border-border bg-surface">
      <div className="flex h-10 items-center border-b border-border">
        <div className="w-[200px] shrink-0 px-150">
          <Skeleton width={72} height={12} />
        </div>
        <div className="w-[180px] shrink-0 px-150">
          <Skeleton width={64} height={12} />
        </div>
        <div className="grow px-150">
          <Skeleton width={48} height={12} />
        </div>
      </div>
      {rows.map((row, i) => (
        <div key={i} className="flex h-9 items-center border-b border-border last:border-b-0">
          <div className="flex w-[200px] shrink-0 items-center gap-100 px-150">
            <Skeleton shape="avatar" />
            <Skeleton width={row.name} />
          </div>
          <div className="w-[180px] shrink-0 px-150">
            <Skeleton width={row.email} />
          </div>
          <div className="grow px-150">
            <Skeleton shape="pill" width={row.status} />
          </div>
        </div>
      ))}
    </SkeletonGroup>
  );
}

Record page

A record while it loads: the breadcrumb bar, the title, the tabs and a few lines, beside the details rail.

import { Skeleton, SkeletonGroup } from '@/ui/skeleton';

export default function SkeletonRecordPage() {
  return (
    <SkeletonGroup label="Loading Halcyon Bank" className="flex w-full flex-col overflow-hidden rounded-xlarge border border-border bg-surface">
      <div className="flex h-14 items-center border-b border-border px-300">
        <Skeleton width={160} height={16} />
      </div>
      <div className="flex flex-col sm:flex-row">
        <div className="flex min-w-0 grow flex-col gap-200 p-300">
          <Skeleton width={256} height={32} className="max-w-full rounded-medium" />
          <div className="flex gap-200">
            <Skeleton width={64} height={16} />
            <Skeleton width={64} height={16} />
            <Skeleton width={64} height={16} />
          </div>
          <Skeleton height={16} />
          <Skeleton width="92%" height={16} />
          <Skeleton width="76%" height={16} />
        </div>
        <div className="flex w-full shrink-0 flex-col gap-150 border-t border-border px-[20px] py-300 sm:w-[240px] sm:border-t-0 sm:border-s">
          <Skeleton width={80} height={20} />
          <Skeleton height={16} />
          <Skeleton height={16} />
          <div className="flex gap-075">
            <Skeleton shape="pill" width={56} />
            <Skeleton shape="pill" width={72} />
          </div>
        </div>
      </div>
    </SkeletonGroup>
  );
}

On this page