caliper uidocs

Stagger

Free

Children of a group arrive one after another on mount or when scrolled into view, so a new list reads in order.

Installs components/ui/stagger.tsxDepends on none beyond ReactMotion shared motion tokens
pnpm dlx shadcn@latest add https://caliperui.site/r/stagger.json
Invoice INV-2041 · €4,800
Invoice INV-2040 · €1,250
Credit note CN-118 · −€300
Invoice INV-2039 · €9,900
Invoice INV-2038 · €620

When to use #

Use it for

  • Search results, cards or rows that appear together.
  • A section's items on first view.

Skip it when

  • Content people come back to every day; arrive once, then stay still.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/stagger.json

Adds its 2 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.

Usage #

This is the code behind the preview above.

example.tsx
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Stagger } from "@/components/ui/stagger"

export function StaggerDemo() {
  const [round, setRound] = useState(0);
  const results = ["Invoice INV-2041 · €4,800", "Invoice INV-2040 · €1,250", "Credit note CN-118 · −€300", "Invoice INV-2039 · €9,900", "Invoice INV-2038 · €620"];
  return (
    <div className="grid w-full max-w-sm gap-3">
      <Button size="sm" variant="secondary" className="w-fit" onClick={() => setRound((value) => value + 1)}>Search again</Button>
      <Stagger replayKey={round} className="grid gap-2">
        {results.map((row) => (
          <div key={row} className="rounded-[10px] px-3 py-2.5 text-sm shadow-[inset_0_0_0_1px_var(--border)]">{row}</div>
        ))}
      </Stagger>
    </div>
  );
}

API reference #

Stagger

component

Also accepts the native props of its root element and a ref.

PropTypeDefaultDescription
trigger"view" | "mount""mount"Start on mount, or the first time the group is in view.
stepnumber45Milliseconds between one child and the next.
replayKeystring | number—Change it to play the entrance again, e.g. after a new search.

Accessibility #

It is built on native elements, so roles and focus come from the browser.

No custom key handling; Tab and the native activation keys do the work.

Motion #

Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.