WebberUI

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.

How to install Pro components →See the plans →

Loading preview…
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.

0.6
24
<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

PropTypeDefaultDescription
childrenReact.ReactNodePage content
transitionKeystring | numberThe 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
durationnumber0.6Total duration of the whole transition (seconds), split evenly between exit and enter
distancenumber24Offset distance for the slide variant (px)
overlayClassNamestringAppended to the overlay className (cover / wipe), so you can override its background color
classNamestringAppended to the outer container className
onTransitionComplete() => voidCallback 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: cover slides a solid panel in to cover, then slides it out from the opposite side to reveal; wipe sweeps across by scaling along the axis, switching transform-origin automatically 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 transitionKey changes.
  • Rapid successive changes: changing transitionKey again 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 with aria-hidden and is pointer-events-none so it does not intercept interaction.

On this page