Mischief

60 / Feedback

Skeleton

A placeholder the shape of what is coming, so the page does not jump when it arrives.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/skeleton
import { Skeleton } from "mischief-ui/skeleton"

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/skeleton/skeleton.tsx
import * as React from "react" import { cn } from "@/lib/utils" export type SkeletonProps = Omit<  React.HTMLAttributes<HTMLDivElement>,  "children"> & {  /** Render this many bars, the last one short, as a block of text would be. */  lines?: number} /** * A placeholder the shape of what is coming, so a page does not jump when it

Usage

export function Card({ article }) {
  if (!article) {
    return (
      <div aria-busy="true" className="grid gap-3">
        <Skeleton className="h-40" />
        <Skeleton lines={3} />
      </div>
    )
  }

  return <Article {...article} />
}

Shaped like the thing it stands in for

A skeleton earns its place by taking the room the content will take. One that is the wrong size moves the page twice: once when it appears and again when it is replaced, which is worse than an empty space that fills in.

So give it the height you know: the avatar is a circle of a fixed size, the card is as tall as a card. Where the length is genuinely unknown, lines renders a block of bars with a short last one, which is what a paragraph looks like from across the room.

<Skeleton className="size-10 rounded-full" />
<Skeleton className="h-40" />
<Skeleton lines={3} />

Saying it out loud

The bars are hidden from assistive technology, because a screen reader has nothing to gain from a description of grey rectangles. That means the waiting is invisible unless you say so: mark the region aria-busy while it loads, and the reader is told to wait rather than hearing an empty container.

API

linesnumberRender this many bars, the last one short, as text would be.
classNamestringThe shape: a height, a width, a radius.

Accessibility

Hidden from assistive technology, because a shape standing in for content is not content. Put aria-busy on the region that is filling, so the wait is announced once by the thing that knows about it rather than by every bar. The pulse stops under reduced motion.