Skeleton
A shape standing in for content that has not arrived.
import { Div } from "@implementjs/core";
import { Skeleton } from "@/lib/components/ui/skeleton";
export default function SkeletonDemo() {
return Div(
{ class: "flex w-full max-w-sm items-center gap-4" },
Skeleton({ class: "size-12 shrink-0 rounded-full" }),
Div(
{ class: "flex flex-1 flex-col gap-2" },
Skeleton({ class: "h-4 w-full" }),
Skeleton({ class: "h-4 w-2/3" }),
),
);
}Installation
npx jsrepo add @implementjs/ui/skeleton
Nothing else comes with it — this one stands alone on @implementjs/core and @implementjs/primitives.
Copy the file below to src/lib/components/ui/skeleton.ts. It imports cn, so copy utils.ts to src/lib/utils.ts too.
import { Div, type Child, type ElementProps } from "@implementjs/core";
import { createComponent } from "@implementjs/primitives";
import { cn } from "@/lib/utils";
export type SkeletonProps = ElementProps<"div">;
/**
* A shape standing in for content that has not arrived. Size it with the
* classes the real content will have, so the swap does not move the layout.
*/
export const Skeleton = createComponent(function Skeleton(
{ class: className, ...props }: SkeletonProps,
...children: Child[]
) {
return Div(
{
...props,
"data-slot": "skeleton",
class: cn("animate-pulse rounded-md bg-accent motion-reduce:animate-none", className),
},
...children,
);
});Usage
import { Skeleton } from "@/lib/components/ui/skeleton";
Skeleton({ class: "h-4 w-48" });
Skeleton({ class: "size-12 rounded-full" });
There are no size props: give it the classes the real content will have, so the swap does not move the layout.
Matching the real thing
A skeleton is worth having only if it is the same size as what replaces it. Build it from the same classes:
// the real row
Div({ class: "flex items-center gap-4" }, Avatar({ class: "size-12" }), Span("Aidan Bleser"));
// its placeholder
Div(
{ class: "flex items-center gap-4" },
Skeleton({ class: "size-12 rounded-full" }),
Skeleton({ class: "h-4 w-32" }),
);
Reduced motion
The pulse is turned off under prefers-reduced-motion — the shape stays, the animation goes.
API Reference
Skeleton
A shape standing in for content that has not arrived. Size it with the classes the real content will have, so the swap does not move the layout. Renders a Div; extra props are forwarded onto it.