Skip to content

Page Transition Wrapper

Wraps route content so it animates out and the next page animates in instead of swapping instantly.

Prominimaleditorial
Category
Animations
Added
2026-09-01
Deps
1
Updated
2026-09-01

This is a viberdy Pro component

The live preview, the props panel and the write-up are open to everyone. The full source, the AI prompt and CLI install are part of Pro.

Preview

Home page

Props

0.35
Requires framer-motion
"use client";

import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
import type { ReactNode } from "react";

const VARIANTS = {
  fade: { initial: { opacity: 0, x: 0, scale: 1 }, exit: { opacity: 0, x: 0, scale: 1 } },
  slide: { initial: { opacity: 0, x: 24, scale: 1 }, exit: { opacity: 0, x: -24, scale: 1 } },
  scale: { initial: { opacity: 0, x: 0, scale: 0.96 }, exit: { opacity: 0, x: 0, scale: 1.03 } },
} as const;

export function PageTransitionWrapper({
  children,
  transitionKey,
  variant = "slide",
  duration = 0.35,
}: {
  children: ReactNode;

  // ---------------------------------------------------------------
  // Full source available with a viberdy Pro subscription.
  // https://viberdy.dev/pricing
  // ---------------------------------------------------------------
}
Notes

About this animation

Page Transition Wrapper animates opacity plus one transform (an x translate or a scale, depending on variant) between two full page-level nodes — never top/left/width, which would force layout across the whole page on every frame. Reach for this instead of a bare CSS transition because a route swap replaces the DOM node entirely; CSS transitions cannot animate an element that's about to unmount and be replaced by an unrelated one, but AnimatePresence keeps the outgoing node mounted just long enough to finish its exit animation before removing it. The non-obvious detail is mode=\"wait\": it's what makes the effect read as one continuous wipe rather than a cross-fade, because it holds the incoming page's enter animation until the outgoing page has fully exited, rather than running both at once. The caveat: every page passed through this wrapper needs a stable, unique key (in practice the route pathname) — without it React treats unrelated pages as the same instance and silently skips the transition.

Related

Pairs well with

Matched on shared tags and category — the entries most likely to be used alongside this one.

Pro

A wrapper that reveals its children one after another as the group scrolls into view.

staggerrevealscroll
Pro

Reveals text one line at a time, each sliding up from behind its own clipping mask.

text-revealmaskscroll
Featured
New

A depth system where layers separate under the pointer from one shared motion source.

parallaxpointerdepth