Back to work
tutorial//2 min read

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

  1. Use layoutId for shared element transitions between routes
  2. Keep animations under 300ms for interactions, up to 500ms for page transitions
  3. Always respect prefers-reduced-motion
  4. 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.