Crafting Smooth Page Transitions
A deep dive into building performant, elegant page transitions using Framer Motion and Next.js App Router.
Page transitions are one of those details that separate a good interface from a great one. When done right, they're invisible — the user simply feels that the app is fluid. When done wrong, they create a jarring, disconnected experience.
The approach
The key insight is to animate layout, not just opacity. A simple fade feels flat. But when elements shift, scale, and settle into their new positions with spring physics, the interface feels alive.
<motion.div
layoutId="work-card"
transition={{ type: "spring", stiffness: 300, damping: 30 }}
>
{children}
</motion.div>
Why spring physics matter
Springs model real-world motion. Unlike cubic-bezier easing, springs don't have a fixed duration — they settle naturally based on stiffness and damping. This makes animations feel organic rather than mechanical.
Stiffness vs. Damping
- High stiffness, low damping → bouncy, energetic
- Low stiffness, high damping → slow, heavy
- Medium stiffness, high damping → snappy, precise (usually what you want for UI)
Implementation tips
- Use
layoutIdfor shared element transitions between routes - Keep animations under 300ms for interactions, up to 500ms for page transitions
- Always respect
prefers-reduced-motion - Test on lower-end devices — 60fps is non-negotiable
The best transitions are the ones users don't notice. They just make everything feel right.