Page Transition
A route transition container combining View Transitions concepts with overlay reveals, playing one of four transitions — fade, slide, cover, or wipe — whenever the route changes.
This is a WebberUI Pro component
Free during the launch campaign: sign up or sign in, then hit “Copy install command” in the preview above and it installs straight away — no payment, no credit card. The command below returns 401 while you are signed out.
npx shadcn@latest add "https://webberui.com/r/page-transition.json?t=<install token>"Playground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<PageTransition />
Installation
npx shadcn@latest add "https://webberui.com/r/page-transition.json?t=<install token>"Or, once registries are configured in components.json, install it as @webberui/page-transition.
Usage
Wrap your page content in PageTransition and pass the value that changes with the route (usually the pathname) to transitionKey. In the Next.js App Router, the most natural place for this is template.tsx:
"use client";
import { usePathname } from "next/navigation";
import { PageTransition } from "@/components/ui/page-transition";
export default function Template({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
return (
<PageTransition transitionKey={pathname} variant="cover" direction="right">
{children}
</PageTransition>
);
}Native View Transitions
If you want navigation to trigger the browser's native View Transitions (cross-page crossfades, shared-element morphs), wrap the update that changes the DOM with useViewTransition:
"use client";
import { useRouter } from "next/navigation";
import { useViewTransition } from "@/components/ui/page-transition";
export function NavLink({ href, children }: { href: string; children: React.ReactNode }) {
const router = useRouter();
const startTransition = useViewTransition();
return (
<a
href={href}
onClick={(event) => {
event.preventDefault();
startTransition(() => router.push(href));
}}
>
{children}
</a>
);
}Browsers without View Transitions support simply run the update directly, degrading gracefully.
Props
PageTransition
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | Page content |
transitionKey | string | number | — | The transition trigger key; changing it plays the transition once (usually the pathname) |
variant | "fade" | "slide" | "cover" | "wipe" | "fade" | Transition style |
direction | "up" | "down" | "left" | "right" | "up" | Direction of the offset or the cover sweep |
duration | number | 0.6 | Total duration of the whole transition (seconds), split evenly between exit and enter |
distance | number | 24 | Offset distance for the slide variant (px) |
overlayClassName | string | — | Appended to the overlay className (cover / wipe), so you can override its background color |
className | string | — | Appended to the outer container className |
onTransitionComplete | () => void | — | Callback fired when the transition has completely finished |
useViewTransition()
Returns startTransition(update), where update is a function that changes the DOM (either synchronous or returning a Promise), and the return value is a Promise<void> that resolves when the transition ends.
How it works
- State-machine driven: internally it runs a three-stage state machine — exit → swap the content seamlessly at the peak of the cover → enter — advanced entirely by Motion's
onAnimationComplete, with no timers involved. - Overlay reveal:
coverslides a solid panel in to cover, then slides it out from the opposite side to reveal;wipesweeps across by scaling along the axis, switchingtransform-originautomatically between covering and revealing. The content swap happens at the exact moment the panel fully covers the view, so nothing visible jumps. - No animation on first load: the initial render matches the SSR output, and a transition only plays after
transitionKeychanges. - Rapid successive changes: changing
transitionKeyagain mid-transition jumps straight to the newest content once the current transition ends, rather than stacking up playbacks.
Accessibility
- When the user has "reduce motion" enabled at the system level, the content is swapped instantly with no offset or cover animation played at all.
- While a transition is running the outer container carries
aria-busy, and the overlay is hidden from assistive technology witharia-hiddenand ispointer-events-noneso it does not intercept interaction.
Preloader Collection
Three preloader overlay animations — text reveal, staircase slide, and pixel dissolve — each revealing the content with its own signature exit once loading finishes.
Confetti Feedback
A hand-built particle confetti celebration — bursting from an origin point, each piece with its own initial velocity, gravity, rotation, and drift, updated on rAF and reclaimed once it lands.