pnpm dlx @glinui/cli@latest add skeletonShipped the new billing page today. The empty states took longer than the happy path, but they feel much better now.
Installation
pnpm dlx @glinui/cli@latest add skeletonPrefer the package?
Usage
Variants
import { Skeleton } from "@glinui/ui"
export function Demo() { return ( <div className="grid gap-3 sm:grid-cols-2"> <Skeleton variant="glinr" className="h-6 w-full" /> <Skeleton variant="solid" className="h-6 w-full" /> <Skeleton variant="plain" className="h-6 w-full" /> <Skeleton variant="soft" className="h-6 w-full" /> <Skeleton variant="outline" className="h-6 w-full" /> <Skeleton variant="ghost" className="h-6 w-full" /> </div> )}Loading to loaded
Swap the skeleton for real content and flip aria-busy.
import { useState } from "react"import { Avatar, Button, Card, Skeleton } from "@glinui/ui"
export function Demo() { const [loading, setLoading] = useState(true) return ( <div className="flex w-full max-w-sm flex-col gap-4"> <Button variant="outline" size="sm" className="w-fit" onClick={() => setLoading((value) => !value)}> {loading ? "Show loaded state" : "Show loading state"} </Button> <Card className="flex items-center gap-4" aria-busy={loading}> {loading ? ( <> <Skeleton className="size-12 rounded-full" /> <div className="flex flex-1 flex-col gap-2"> <Skeleton className="h-4 w-2/3" /> <Skeleton className="h-3 w-1/2" /> </div> </> ) : ( <> <Avatar fallback="JO" size="lg" tone="success" /> <div className="flex flex-col"> <span className="font-medium">Jonas Okafor</span> <span className="text-sm text-[var(--color-muted)]">Engineering manager</span> </div> </> )} </Card> </div> )}Variants
Six visual variants to match any surface context.
import { Skeleton } from "@glinui/ui"
export function SkeletonVariantDemo() {
return (
<div className="space-y-4">
<div className="space-y-1">
<span className="text-xs font-medium text-neutral-500 dark:text-neutral-400">Default</span>
<Skeleton variant="default" className="h-5 w-64" />
</div>
<div className="space-y-1">
<span className="text-xs font-medium text-neutral-500 dark:text-neutral-400">Glass</span>
<Skeleton variant="glass" className="h-5 w-64" />
</div>
<div className="space-y-1">
<span className="text-xs font-medium text-neutral-500 dark:text-neutral-400">Liquid</span>
<Skeleton variant="liquid" className="h-5 w-64" />
</div>
<div className="space-y-1">
<span className="text-xs font-medium text-neutral-500 dark:text-neutral-400">Matte</span>
<Skeleton variant="matte" className="h-5 w-64" />
</div>
<div className="space-y-1">
<span className="text-xs font-medium text-neutral-500 dark:text-neutral-400">Outline</span>
<Skeleton variant="outline" className="h-5 w-64" />
</div>
<div className="space-y-1">
<span className="text-xs font-medium text-neutral-500 dark:text-neutral-400">Ghost</span>
<Skeleton variant="ghost" className="h-5 w-64" />
</div>
</div>
)
}Sizes
import { Skeleton } from "@glinui/ui"
export function SkeletonSizeDemo() {
return (
<div className="space-y-3">
<Skeleton size="sm" className="w-40" />
<Skeleton size="md" className="w-56" />
<Skeleton size="lg" className="w-72" />
</div>
)
}Card Loading
Compose multiple skeletons to mirror real card layouts during async fetch.
import { Skeleton } from "@glinui/ui"
export function SkeletonCardDemo() {
return (
<div className="max-w-sm rounded-2xl border border-neutral-200 p-4 dark:border-neutral-800">
<Skeleton className="h-40 w-full rounded-xl" />
<div className="mt-4 space-y-3">
<Skeleton className="h-5 w-3/4" />
<Skeleton className="h-4 w-full" />
<Skeleton className="h-4 w-5/6" />
</div>
<div className="mt-5 flex items-center gap-3">
<Skeleton className="h-9 w-9 rounded-full" />
<div className="space-y-2">
<Skeleton className="h-3.5 w-24" />
<Skeleton className="h-3 w-16" />
</div>
</div>
</div>
)
}Profile Loading
Avatar, name, and bio placeholders for a user profile state.
import { Skeleton } from "@glinui/ui"
export function SkeletonProfileDemo() {
return (
<div className="flex items-start gap-4">
<Skeleton className="h-16 w-16 shrink-0 rounded-full" />
<div className="flex-1 space-y-3 pt-1">
<Skeleton className="h-5 w-40" />
<Skeleton className="h-3.5 w-56" />
<div className="flex gap-4 pt-1">
<Skeleton className="h-3 w-20" />
<Skeleton className="h-3 w-20" />
<Skeleton className="h-3 w-20" />
</div>
</div>
</div>
)
}Media Grid
Image grid placeholder for gallery or feed views.
import { Skeleton } from "@glinui/ui"
export function SkeletonMediaGridDemo() {
return (
<div className="grid grid-cols-3 gap-3">
<Skeleton className="aspect-square w-full rounded-xl" />
<Skeleton className="aspect-square w-full rounded-xl" />
<Skeleton className="aspect-square w-full rounded-xl" />
<Skeleton className="col-span-2 aspect-video w-full rounded-xl" />
<Skeleton className="aspect-square w-full rounded-xl" />
</div>
)
}Glass Card Loading
Skeleton on a glass surface for overlay loading states.
import { Skeleton } from "@glinui/ui"
export function SkeletonGlassDemo() {
return (
<div className="max-w-sm rounded-2xl border border-white/20 bg-white/30 p-5 backdrop-blur-xl dark:border-white/10 dark:bg-white/5">
<div className="flex items-center gap-3">
<Skeleton variant="glass" className="h-11 w-11 rounded-full" />
<div className="space-y-2 flex-1">
<Skeleton variant="glass" className="h-4 w-32" />
<Skeleton variant="glass" className="h-3 w-20" />
</div>
</div>
<div className="mt-4 space-y-2.5">
<Skeleton variant="glass" className="h-3.5 w-full" />
<Skeleton variant="glass" className="h-3.5 w-full" />
<Skeleton variant="glass" className="h-3.5 w-3/4" />
</div>
<div className="mt-4 flex gap-2">
<Skeleton variant="glass" className="h-9 w-24 rounded-xl" />
<Skeleton variant="glass" className="h-9 w-24 rounded-xl" />
</div>
</div>
)
}List Loading
Row-based skeleton for table or list views.
import { Skeleton } from "@glinui/ui"
export function SkeletonListDemo() {
return (
<div className="max-w-md divide-y divide-neutral-200 dark:divide-neutral-800">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="flex items-center gap-3 py-3">
<Skeleton className="h-10 w-10 shrink-0 rounded-lg" />
<div className="flex-1 space-y-2">
<Skeleton className="h-4 w-3/5" />
<Skeleton className="h-3 w-2/5" />
</div>
<Skeleton className="h-8 w-16 rounded-lg" />
</div>
))}
</div>
)
}Accessibility
- Sets `aria-hidden` when decorative.
- Uses `motion-safe:animate-pulse` with `motion-reduce:animate-none`.
- Decorative placeholders default to `aria-hidden` so screen readers skip them.
- Set `decorative={false}` and add `role="status"` with `aria-live="polite"` for semantic loading regions.
- Avoid stacking more than 5-6 skeletons without a loading boundary to limit screen reader noise.
ARIA Attributes
- `aria-hidden="true"` when decorative
- `aria-busy="true"` on parent container
Reduced Motion
Pulse animation disabled with `prefers-reduced-motion: reduce`.
Shimmer animation uses `motion-safe:animate-[...]` / `motion-reduce:hidden`. Under `prefers-reduced-motion`, the shimmer overlay is hidden entirely and the skeleton renders as a static muted shape.
Affected properties
opacityAPI Reference
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
variant | ControlVariantProp | No | ambient (glinr) | Placeholder look. Omit to follow the ambient design style. Glass is opt-in. |
size | "sm" | "md" | "lg" | No | md | Height (h-4/h-6/h-10). |
decorative | boolean | No | true | When true, sets aria-hidden. |
Source
Import directly from the package or browse the source on GitHub. Click any file to view it.
Import
import { Skeleton } from "@glinui/ui"Source Files
Dependencies
@glinui/uiGenerated API Snapshot
Beta
Generated API Snapshot
BetaAuto-extracted from TypeScript source in packages/ui/src/components/skeleton.tsx. This section is in beta and may lag behind hand-curated docs. Regenerate with pnpm --filter @glinui/docs api:generate.
Generated: 2026-10-08T01:41:21.546Z · Full index: /docs/api-metadata
Primary Props Type
SkeletonProps
SkeletonProps
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
decorative | boolean | No | true | - |
size | "sm" | "md" | "lg" | No | "md" | Variant option from skeletonVariants. |
variant | ControlVariantProp | No | - | Placeholder look. Omit to follow the ambient design style (glinr = inset well). Same vocabulary as Input. |