Shared Learning

Updated on June 8, 2026

The Next.js App Router introduced incredible performance gains, but it also aggressively broke traditional page transitions by instantly swapping out the DOM on navigation. For development teams building large-scale, highly interactive creative projects, this is an immediate roadblock. 

 

In the React ecosystem, the default solution for exit animations is Framer Motion. But when your team requires complex timeline choreography, or you want to keep your animation logic entirely framework-agnostic, you need the firepower of a tool like GSAP. The problem? You immediately lose Framer Motion's AnimatePresence feature, which is what stops React from destroying a component before its exit animation finishes.

 

By Scott Agrimson, Senior Frontend Developer, Metajive

To solve this at Metajive, we didn't wait for a patch or a third-party dependency. We engineered our own completely standalone AnimatePresence system, combined it with a "frozen router" technique, and restored full page transitions to the Next.js App Router. Here is exactly how we built it.

Framer Motion is an excellent tool, and its AnimatePresence component is arguably its best feature. However, at the enterprise level, technical decisions are about long-term flexibility and team velocity.

We require highly interactive architectures to be framework-agnostic. If a project eventually moves off React, or if we are building a hybrid application, our core animation logic needs to transfer seamlessly. We are not interested in being locked into a single ecosystem. We needed a way to recreate the magic of AnimatePresence without tying it to any specific animation library.

React’s default behavior is ruthless: when a component is removed from the render tree, it disappears instantly. To run exit animations, we have to hijack that lifecycle. We need to tell React, "Hold up, let this element finish doing its thing, and then you can remove the node."

We solved this by creating a custom AnimatePresence component that maintains a rendered array in state. Instead of letting React destroy the child immediately, we intercept the unmount, flag the child as isPresent: false, and pass down a safeToRemove callback via a React Context Provider.

Here is the most powerful part of this architecture: our custom AnimatePresence component doesn't actually know what GSAP is. It is a completely standalone, animation-framework-agnostic implementation of Framer Motion’s concept

It is a pure React lifecycle manager. While we often pair it with GSAP for complex choreography, a developer could just as easily use standard CSS transitions, anime.js, or the Web Animations API. The only requirement is that the child component watches the isPresent boolean and calls safeToRemove() when its native animation finishes.

Furthermore, the parent route will not officially unmount and clear the DOM until every single animating child component has fired its specific safeToRemove function, guaranteeing a clean, orchestrated exit without duplicate elements stacking up.

Having a working AnimatePresence component is only half the battle. The Next.js App Router’s immediate context swapping will still break route-level transitions.

To restore page transitions, you must freeze the routing context of the previous page while the new page loads. We achieve this by tapping directly into Next.js's internal shared runtime and capturing the LayoutRouterContext with a useRef.

We wrap this FrozenRouter inside our custom AnimatePresence. Crucially, to prevent React from crashing due to duplicate keys when a user clicks links rapidly, we append Date.now() to the current pathname. This ensures every navigation event generates a uniquely keyed instance of the route.

With the standalone infrastructure in place, the developer experience becomes incredibly straightforward. Because we prefer GSAP, engineers simply use the official @gsap/react useGSAP hook alongside our custom usePresence hook.

There is no complex boilerplate per component. You watch the isPresent boolean. If it's true, play the enter animation. If it flips to false, play the exit animation and pass safeToRemove to GSAP's onComplete callback.

You do not have to settle for the default tools if they don't fit your project's architectural needs. By understanding the underlying mechanics of React lifecycles and Next.js routing, you can engineer solutions that provide the exact creative control your team requires.

By rebuilding AnimatePresence as a pure, standalone lifecycle manager and utilizing a frozen router context, we successfully merged the uncompromised timeline power of tools like GSAP with the performance benefits of the Next.js App Router. The result is an elite, framework-agnostic animation system that scales.

Newsletter Sign-up

A newsletter you won’t ignore. 

Understanding the landscape and the challenges ahead is crucial for making informed decisions.