the shape of something still coming, breathing slowly while it loads
npx shadcn add @ag/skeletonAlready installed? Preview an update
npx shadcn add @ag/skeleton --dry-runYour installed source stays unchanged until you update it. How updates work
Latest changes · 2026-10-10
- New: the shape of something still coming, breathing slowly while it loads, still with reduced motion, hidden from a screen reader; plain or a pane of glass.
the demo above, verbatim
"use client"
import { Sample } from "@/app/samples"
import { MaterialProvider } from "@/registry/base-nova/ui/material"
import { Skeleton } from "@/registry/base-nova/ui/skeleton"
// #region post
function Post() {
return (
<div aria-busy className="flex w-64 items-center gap-3">
{/* A glass skeleton states its radius as a length (half the height, for a
round one): it places the glass's light as well as its corners. */}
<Skeleton className="size-10 shrink-0 rounded-full [--glass-radius:1.25rem]" />
<div className="flex flex-1 flex-col gap-2">
<Skeleton className="h-3 w-3/4 [--glass-radius:6px]" />
<Skeleton className="h-3 w-1/2 [--glass-radius:6px]" />
</div>
</div>
)
}
// #endregion
export default function SkeletonDemo() {
return (
<div className="space-y-8">
<Sample name="materials" title="a post still coming" with="post">
<div className="grid gap-4 sm:grid-cols-2">
<Post />
<MaterialProvider material="glass">
<div className="rounded-xl bg-[url(/lightbox/shore.jpg)] bg-cover bg-center p-6">
<Post />
</div>
</MaterialProvider>
</div>
</Sample>
<Sample name="post" glance title="one post, still coming" with="post">
<Post />
</Sample>
</div>
)
}the source, every file an install writes
"use client"
import type * as React from "react"
import { cn } from "@/lib/utils"
import {
type Material,
type Scheme,
useMaterial,
useSurfaceScheme,
} from "@/registry/base-nova/ui/material"
import { glassVariants, schemeOf } from "@/registry/base-nova/ui/material-glass"
// The shape of something still coming: a block where it will be, breathing slowly
// while it loads and gone once it has (it is shown only while the work runs, so it
// never breathes for nothing). Size it as what it stands for (`h-4 w-40` a line,
// `size-10 rounded-full [--glass-radius:1.25rem]` a face: the glass states its
// radius as a length, which places its light too). Hidden from a screen reader: the
// region that waits says so (`aria-busy`). With reduced motion it holds still.
// `material="glass"`: a pane of the glass the content will sit on.
export type SkeletonProps = React.ComponentProps<"div"> & {
material?: Material
/** The colour scheme it stands in (material): default the nearest provider's. */
scheme?: Scheme
}
export function Skeleton({
material,
scheme,
className,
ref,
...props
}: SkeletonProps) {
const m = useMaterial(material)
const [s, here] = useSurfaceScheme<HTMLDivElement>(scheme, ref)
return (
<div
ref={here}
aria-hidden
data-slot="skeleton"
data-material={m}
className={cn(
"animate-pulse [animation-duration:2s] motion-reduce:animate-none",
schemeOf(s),
m === "glass"
? [
glassVariants({ shape: "surface" }),
"[--glass-radius:--theme(--radius-xl)]",
]
: "rounded-md bg-foreground/8",
className,
)}
{...props}
/>
)
}