Skeleton

Loading placeholder components for indicating content is being loaded.

Import

import { Skeleton, ChartSkeleton, TableSkeleton, FormSkeleton } from '@/components/ui';

Basic Usage

<Skeleton className="h-4 w-32" />

Components

Component Description
Skeleton Base skeleton element
ChartSkeleton Pre-built chart placeholder
TableSkeleton Pre-built table placeholder
FormSkeleton Pre-built form placeholder

Skeleton Props

Prop Type Default Description
className string - Size and shape classes

Basic Shapes

{/* Text line */}
<Skeleton className="h-4 w-full" />

{/* Short line */}
<Skeleton className="h-4 w-32" />

{/* Avatar circle */}
<Skeleton className="h-10 w-10 rounded-full" />

{/* Image placeholder */}
<Skeleton className="h-48 w-full" />

{/* Button placeholder */}
<Skeleton className="h-10 w-24 rounded-lg" />

Card Skeleton

export function CardSkeleton() {
  return (
    <Card className="p-6 space-y-4">
      <div className="flex items-center gap-4">
        <Skeleton className="h-12 w-12 rounded-full" />
        <div className="space-y-2">
          <Skeleton className="h-4 w-32" />
          <Skeleton className="h-3 w-24" />
        </div>
      </div>
      <Skeleton className="h-4 w-full" />
      <Skeleton className="h-4 w-3/4" />
    </Card>
  );
}

Pre-built Skeletons

ChartSkeleton

<ChartSkeleton />
<ChartSkeleton height={400} />

TableSkeleton

<TableSkeleton />
<TableSkeleton rows={10} />

FormSkeleton

<FormSkeleton />

Loading State Pattern

'use client';
import { useQuery } from '@tanstack/react-query';
import { Skeleton, Card } from '@/components/ui';

export function UserCard({ userId }) {
  const { data: user, isLoading } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });

  if (isLoading) {
    return (
      <Card className="p-6">
        <div className="flex items-center gap-4">
          <Skeleton className="h-12 w-12 rounded-full" />
          <div className="space-y-2">
            <Skeleton className="h-5 w-32" />
            <Skeleton className="h-4 w-24" />
          </div>
        </div>
      </Card>
    );
  }

  return (
    <Card className="p-6">
      <div className="flex items-center gap-4">
        <Avatar src={user.avatar} name={user.name} size="lg" />
        <div>
          <h3 className="font-semibold">{user.name}</h3>
          <p className="text-sm text-zinc-500">{user.email}</p>
        </div>
      </div>
    </Card>
  );
}

List Skeleton

export function ListSkeleton({ count = 5 }) {
  return (
    <div className="space-y-4">
      {Array.from({ length: count }).map((_, i) => (
        <div key={i} className="flex items-center gap-4">
          <Skeleton className="h-10 w-10 rounded-full" />
          <div className="flex-1 space-y-2">
            <Skeleton className="h-4 w-1/2" />
            <Skeleton className="h-3 w-1/3" />
          </div>
        </div>
      ))}
    </div>
  );
}

Dashboard Skeleton

export function DashboardSkeleton() {
  return (
    <div className="space-y-6">
      {/* Stats */}
      <div className="grid grid-cols-4 gap-4">
        {Array.from({ length: 4 }).map((_, i) => (
          <Card key={i} className="p-6">
            <Skeleton className="h-4 w-24 mb-2" />
            <Skeleton className="h-8 w-32" />
          </Card>
        ))}
      </div>

      {/* Chart */}
      <ChartSkeleton />

      {/* Table */}
      <TableSkeleton rows={5} />
    </div>
  );
}

Styling