Skeleton
Shimmering placeholders for loading content.
$
pnpm dlx shadcn@latest add https://lodeui.com/r/skeleton.jsoncomponents/examples/skeleton-demo.tsx
"use client";
import * as React from "react";
import { Button } from "@/components/ui/button";
import { Skeleton, SkeletonText } from "@/components/ui/skeleton";
export default function SkeletonDemo() {
const [loading, setLoading] = React.useState(true);
return (
<div className="w-full max-w-sm space-y-4">
<div className="rounded-xl border border-border bg-card p-4">
{loading ? (
<div className="flex gap-3"><Skeleton className="size-10 rounded-full" /><div className="flex-1 space-y-2 pt-1"><Skeleton className="h-3 w-1/2" /><SkeletonText lines={2} /></div></div>
) : (
<div className="flex gap-3"><span className="grid size-10 place-items-center rounded-full bg-muted font-medium">AL</span><div><p className="font-medium">Ada Lovelace</p><p className="text-sm text-muted-foreground">Shipped the analytics engine and wrote the first program.</p></div></div>
)}
</div>
<Button variant="secondary" size="sm" onClick={() => setLoading((l) => !l)}>{loading ? "Show content" : "Show skeleton"}</Button>
</div>
);
}When to use
- Loading states where you know the layout, so content doesn't jump in.
When not to use
- Actions in progress: use a loading button.
- Unknown layout: use a spinner.
Installation
$
pnpm dlx shadcn@latest add https://lodeui.com/r/skeleton.json1Copy the source into your project
components/ui/skeleton.tsximport { cn } from "@/lib/utils"; /** A placeholder block with a moving sheen. Respects reduced motion. */ export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) { return ( <div aria-hidden className={cn( "relative overflow-hidden rounded-md bg-muted", "after:absolute after:inset-0 after:-translate-x-full after:animate-[lode-shimmer_1.6s_infinite] after:bg-gradient-to-r after:from-transparent after:via-foreground/[0.06] after:to-transparent", "motion-reduce:after:hidden", className, )} {...props} /> ); } /** Common shapes: lines of text with a ragged last line. */ export function SkeletonText({ lines = 3, className }: { lines?: number; className?: string }) { return ( <div className={cn("space-y-2", className)}> {Array.from({ length: lines }, (_, i) => ( <Skeleton key={i} className={cn("h-3", i === lines - 1 ? "w-3/5" : "w-full")} /> ))} </div> ); }
Usage
Import it from @/components/ui/skeleton after installing.
example.tsx
import { Skeleton, SkeletonText } from "@/components/ui/skeleton";
export function CardLoading() {
return (
<div role="status" aria-label="Loading" className="space-y-3">
<Skeleton className="h-40 w-full" />
<SkeletonText lines={2} />
</div>
);
}API reference
Skeleton
A shimmering block. Size it with classes.
| Prop | Type | Default | Description |
|---|---|---|---|
| className | string | – | Width, height and radius. |
SkeletonText
Lines of text placeholder; the last one is shorter.
| Prop | Type | Default | Description |
|---|---|---|---|
| lines | number | 3 | Number of lines. |
Accessibility
- Skeletons are aria-hidden. Put role="status" with a label on the wrapper.
Motion
- A soft shimmer sweeps across.
Responsive behavior
- Size with responsive classes to match the real content.
Performance
- A CSS animation; no JavaScript.
Notes for AI
- Match skeleton sizes to the final content to avoid layout shift.
This page as Markdown, for assistants and coding agents:
/components/skeleton/markdownThe full library index for assistants is at /llms.txt.
Lode222 doc pagesllms.txt