Elements

Components / Base

Skeleton

Block-level loading placeholder. Uses the same shimmer sweep animation as the Button loading state so every loading indicator feels visually consistent. Size and shape are set entirely through className.

Installation

bash
npx shadcn add https://raw.githubusercontent.com/levi-putna/elements/main/registry/skeleton/registry.json

Usage

tsx
import { Skeleton } from "@/components/ui/skeleton"

export function CardSkeleton() {
  return (
    <div className="space-y-3">
      <Skeleton className="h-40 w-full" />
      <Skeleton className="h-4 w-3/4" />
      <Skeleton className="h-4 w-1/2" />
    </div>
  )
}

Examples

Card

A card placeholder built from a few sized skeletons.

Card

List

Avatar and two-line text rows: useful for contacts, notifications, or search results.

List
tsx
import { Skeleton } from "@/components/ui/skeleton"

export function ListSkeleton() {
  return (
    <div className="w-full space-y-4">
      {[1, 2, 3, 4].map((item) => (
        <div key={item} className="flex items-center gap-3">
          <Skeleton className="h-10 w-10 shrink-0 rounded-full" />
          <div className="flex-1 space-y-2">
            <Skeleton className="h-4 w-3/5" />
            <Skeleton className="h-3 w-2/5" />
          </div>
        </div>
      ))}
    </div>
  )
}

Table

Header row plus repeating data rows: drop in while table data is fetching.

Table
tsx
import { Skeleton } from "@/components/ui/skeleton"

export function TableSkeleton() {
  return (
    <div className="w-full overflow-hidden rounded-sm border border-border">
      {/* Column headers */}
      <div className="flex gap-4 border-b border-border bg-off-white px-4 py-3">
        <Skeleton className="h-4 w-24" />
        <Skeleton className="h-4 flex-1" />
        <Skeleton className="h-4 w-20" />
        <Skeleton className="h-4 w-28" />
      </div>
      {/* Data rows */}
      {[1, 2, 3, 4, 5].map((row) => (
        <div
          key={row}
          className="flex gap-4 border-b border-border px-4 py-3 last:border-b-0"
        >
          <Skeleton className="h-4 w-24" />
          <Skeleton className="h-4 flex-1" />
          <Skeleton className="h-4 w-20" />
          <Skeleton className="h-4 w-28" />
        </div>
      ))}
    </div>
  )
}

Chat messages

User bubble and assistant reply lines: matches the Message component layout while a response loads.

Chat
tsx
import { Skeleton } from "@/components/ui/skeleton"

export function ChatSkeleton() {
  return (
    <div className="flex w-full flex-col gap-6">
      {/* User message */}
      <div className="ml-auto w-fit rounded-sm bg-secondary px-4 py-3">
        <Skeleton className="h-4 w-48" />
      </div>
      {/* Assistant reply loading */}
      <div className="max-w-[85%] space-y-2">
        <Skeleton className="h-4 w-full" />
        <Skeleton className="h-4 w-5/6" />
        <Skeleton className="h-4 w-4/6" />
      </div>
    </div>
  )
}

Shapes

Avatars, text lines, and image blocks are all the same component with different sizing classes.

tsx
{/* Size and shape are controlled entirely with classNames */}
<Skeleton className="h-10 w-10 rounded-full" />  {/* avatar */}
<Skeleton className="h-4 w-full" />              {/* line of text */}
<Skeleton className="h-48 w-full rounded-md" />  {/* image / card */}

Props

PropTypeDefaultDescription
classNamestring-Sizing and shape classes (height, width, border radius). The shimmer sweep, base background, and overflow clipping are always applied.
...propsHTMLAttributes<HTMLDivElement>-Any other div attributes are spread onto the root element.