Motion UIComponentsComponent

Stagger reveal

An editorial entrance where a headline rises line by line, then followers stagger in behind it.

npx shadcn@latest add @motion/stagger-reveal

API reference

Components

<StaggerReveal />

The orchestration container. Wraps a StaggerRevealHeadline and its StaggerRevealItem followers, and runs the single-cursor mount entrance over them via useStaggerReveal. Server and no-JS output starts visible. On hydration, the layout effect hides the container before paint while it measures and splits the headline, then reveals it for the entrance. Any setup failure restores visibility.

children

ReactNode

The container content: one StaggerRevealHeadline and any number of StaggerRevealItem followers, in the order they should stagger in.

as?

StaggerRevealTag = "div"

The element to render. Defaults to "div".

className?

string

Merged onto the container - this is where the layout utilities live (mx-auto flex flex-col items-start gap-6 px-6, etc).

id?

string

Forwarded to the container element.

<StaggerRevealHeadline />

The split-target heading the container reveals line by line. Renders plain, readable text (the split runs after mount) with the whole-headline aria-label announce. Exactly one belongs inside a StaggerReveal.

children

string

The plain headline text to split and reveal. Must be a string - splitText operates on text content, so this cannot be arbitrary JSX.

as?

StaggerRevealHeadlineTag = "h1"

The element to render. Defaults to "h1".

ariaLabel?

string

The whole-headline announce for assistive tech, read instead of the per-word spans splitText injects. Defaults to children, so the full line is always announced; the container also restores the original text from it on a reduced-motion re-run.

className?

string

Merged onto the heading - all colour/size/weight shadcn utilities live here. A splitText-driven heading carries its own display treatment via the preview theme, so no font-*/tracking utility is needed.

id?

string

Forwarded to the heading (e.g. the aria-labelledby target id).

<StaggerRevealItem />

Tags a follower the container staggers in after the headline. Followers reveal in DOM order, each a beat after the last, on the same running cursor the headline started. Drop as many as the layout needs inside a StaggerReveal.

children

ReactNode

The follower's content (a paragraph of copy, a CTA cluster, a trust line).

as?

StaggerRevealItemTag = "div"

The element to render. Defaults to "div".

className?

string

Merged onto the follower - colour/size/layout shadcn utilities live here.

id?

string

Forwarded to the follower element.

Hooks

useStaggerReveal

The orchestration as a hook, for a consumer who wants to own the container element rather than use StaggerReveal. Attach the returned ref to a container that holds one StaggerRevealHeadline (the split target) and any number of StaggerRevealItem followers. Its layout effect hides the hydrated container until document.fonts.ready resolves and the headline is split, then runs a single delay cursor through the headline lines and followers. Server output remains visible, and any setup failure reveals the original content. Robust to re-runs: a reduced-motion change resets the headline's textContent to the original string before re-splitting, so previous spans are never double-wrapped.

useStaggerReveal(): StaggerRevealHandle

Returns

ref

RefObject<HTMLElement | null>

Attach to the container element. The layout effect temporarily hides it while fonts load and the headline is split; server and no-JS output stays visible so setup failures never remove the content.

Functions

runStaggerPreparation

Runs asynchronous measurement/splitting and always invokes the visible-state fallback. Setup errors are intentionally swallowed because the original, unsplit content is the resilient final state.

runStaggerPreparation(prepare: () => Promise<void>, reveal: () => void): Promise<void>