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.jsonUsage
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
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | - | Sizing and shape classes (height, width, border radius). The shimmer sweep, base background, and overflow clipping are always applied. |
| ...props | HTMLAttributes<HTMLDivElement> | - | Any other div attributes are spread onto the root element. |