caliper uidocs

View transition

Free

Moves between views without a cut: the old view fades or slides out as the new one comes in, with the View Transition API where available.

Installs components/ui/view-transition.tsxDepends on none beyond ReactMotion honours reduced motion
pnpm dlx shadcn@latest add https://caliperui.site/r/view-transition.json
  • Mon · 4 meetings
  • Tue · 2 meetings
  • Wed · Board pack due
  • Thu · Northwind workshop
  • Fri · 1:1 Tomás

When to use #

Use it for

  • Tab content, steps and in-page routes.
  • Switching between related views of the same thing.

Skip it when

  • Every click; keep transitions for changes of place.

Installation #

terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/view-transition.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 { SegmentedControl } from "@/components/ui/segmented-control"
import { ViewTransition } from "@/components/ui/view-transition"

export function ViewTransitionDemo() {
  const [view, setView] = useState("week");
  const [last, setLast] = useState("week");
  const order = ["day", "week", "month"];
  const content: Record<string, string[]> = {
    day: ["09:00 Close stand-up", "11:00 Ostra security review", "15:30 Focus: Q4 forecast"],
    week: ["Mon · 4 meetings", "Tue · 2 meetings", "Wed · Board pack due", "Thu · Northwind workshop", "Fri · 1:1 Tomás"],
    month: ["Week 41 · 11 meetings", "Week 42 · 9 meetings", "Week 43 · Team lunch", "Week 44 · Month-end close"],
  };
  return (
    <div className="grid w-full max-w-sm gap-3">
      <SegmentedControl
        label="Range"
        value={view}
        onValueChange={(next) => {
          setLast(view);
          setView(next);
        }}
        options={[{ value: "day", label: "Day" }, { value: "week", label: "Week" }, { value: "month", label: "Month" }]}
      />
      <ViewTransition viewKey={view} type="slide" direction={order.indexOf(view) >= order.indexOf(last) ? "forward" : "back"}>
        <ul className="grid gap-2">
          {content[view]!.map((row) => <li key={row} className="rounded-[10px] px-3 py-2.5 text-sm shadow-[inset_0_0_0_1px_var(--border)]">{row}</li>)}
        </ul>
      </ViewTransition>
    </div>
  );
}

API reference #

ViewTransition

component
PropTypeDefaultDescription
viewKey *string—Changes when the view changes, e.g. the route or tab id.
type"fade" | "slide""fade"
direction"forward" | "back""forward"For slides: forward comes from the right, back from the left.
classNamestring—
children *ReactNode—

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 #

It checks prefers-reduced-motion itself and settles immediately instead of travelling. Every state is still reachable.