# Skeleton

Shimmering or pulsing placeholder blocks with three preset compositions for product cards, product lists, and category grids.

- Category: Data Display
- License: MIT
- Page: https://www.saasuji.com/components/skeleton
- Install: npx shadcn@latest add https://www.saasuji.com/r/skeleton.json

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| width (Skeleton) | `string | number` | `"100%"` | Width of the placeholder block. |
| height (Skeleton) | `string | number` | `20` | Height of the placeholder block. |
| borderRadius (Skeleton) | `number` | `6` | Corner radius of the placeholder block in pixels. |
| variant (Skeleton) | `"shimmer" | "pulse"` | `"shimmer"` | Shimmer sweep or opacity pulse loading animation. |
| className (Skeleton) | `string` | — | Additional classes merged onto the placeholder block. |
| count (ProductListSkeleton) | `number` | `5` | Number of ProductCardSkeleton rows to render. |
| ProductCardSkeleton, CategorySkeleton | `() => React.ReactElement` | — | Preset compositions that wrap Skeleton; neither accepts any props. |

## Source

```tsx
import { cn } from "@/lib/utils";

interface SkeletonProps {
  width?: string | number;
  height?: string | number;
  borderRadius?: number;
  className?: string;
  variant?: "shimmer" | "pulse";
}

export function Skeleton({ width, height, borderRadius = 6, className, variant = "shimmer" }: SkeletonProps) {
  return (
    <div
      className={cn(
        variant === "shimmer" ? "skeleton-shimmer-bg" : "bg-secondary animate-pulse",
        className
      )}
      style={{
        width: width ?? "100%",
        height: height ?? 20,
        borderRadius,
      }}
    />
  );
}

export function ProductCardSkeleton() {
  return (
    <div className="flex gap-3.5 px-[18px] py-4 rounded-xl skeleton-shimmer-bg border border-border">
      <Skeleton width={56} height={56} borderRadius={12} />
      <div className="flex-1 flex flex-col gap-2">
        <Skeleton width="40%" height={16} />
        <Skeleton width="80%" height={13} />
        <div className="flex gap-1.5">
          <Skeleton width={60} height={20} borderRadius={6} />
          <Skeleton width={50} height={20} borderRadius={6} />
        </div>
      </div>
      <Skeleton width={48} height={52} borderRadius={10} />
    </div>
  );
}

export function ProductListSkeleton({ count = 5 }: { count?: number }) {
  return (
    <div className="flex flex-col gap-0.5">
      {Array.from({ length: count }).map((_, i) => (
        <ProductCardSkeleton key={i} />
      ))}
    </div>
  );
}

export function CategorySkeleton() {
  return (
    <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4">
      {Array.from({ length: 8 }).map((_, i) => (
        <div key={i} className="p-4 rounded-xl skeleton-shimmer-bg border border-border">
          <Skeleton width={40} height={40} borderRadius={10} />
          <div className="mt-3">
            <Skeleton width="70%" height={14} />
          </div>
        </div>
      ))}
    </div>
  );
}

```
