View transition
FreeMoves 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.
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.