eduardweb.
Animații (Framer Motion)Intermediar#nextjs#react#framer-motion#app-router

Cum faci AnimatePresence de la Framer Motion să meargă în Next.js App Router

De Radu Grigore, 27 iun. 2026 · 19 vizualizări · 3 like-uri

Postat 27 iun. 2026
typescript
'use client';

import { useContext, useRef } from 'react';
import { LayoutRouterContext } from 'next/dist/shared/lib/app-router-context.shared-runtime';

export function FrozenRoute({ children }: { children: React.ReactNode }) {
  const context = useContext(LayoutRouterContext);
  const frozen = useRef(context);

  return (
    <LayoutRouterContext.Provider value={frozen.current}>
      {children}
    </LayoutRouterContext.Provider>
  );
}

Dacă ai încercat să pui tranziții de pagină cu Framer Motion în Next.js App Router, probabil ai vrut să-ți arunci laptopul pe geam când ai văzut că animațiile de exit nu funcționează. Problema e veche, dar din fericire există un pattern prin care poți convinge AnimatePresence să-și facă treaba fără să strici tot fluxul de randare.

De ce crapă animația de exit?

În vechiul Pages Router, Next.js păstra componenta în DOM până când Framer Motion îi semnala că animația s-a terminat. În App Router, routing-ul e gestionat diferit. Când schimbi ruta, Next.js demontează (unmounts) instantaneu nodul din DOM și aduce noul server component.

AnimatePresence se uită la copiii săi direct în React tree. Dacă React șterge nodul instant, Framer Motion nu mai are ce să animeze pe ieșire. Rezultatul? Pagina nouă apare cu animația de enter, dar cea veche dispare brusc, fără niciun exit.

Am pățit asta la un proiect mărișor, un dashboard pentru o clinică medicală cu vreo 14k de utilizatori activi lunar. Designerul voia neapărat tranziții fine de tip slide-fade între ecrane. Am pierdut o zi întreagă testând diverse workaround-uri de pe GitHub până am dat de soluția stabilă.

Soluția: Înghețarea contextului de navigare

Pentru a păstra vechea pagină în DOM în timpul animației, trebuie să "păcălim" routerul din Next.js. Putem face asta interceptând LayoutRouterContext-ul intern din React și forțându-l să rețină starea precedentă cât timp rulăm animația de exit.

Folosim un wrapper pe care-l numim FrozenRoute. Acesta folosește un useRef ca să salveze contextul curent de navigare și să-l ofere mai departe doar când vrem noi. De fiecare dată când ruta se schimbă, cheia se modifică, AnimatePresence detectează schimbarea, iar FrozenRoute se asigură că vechiul layout router nu distruge elementele din DOM până nu se termină animația.

Trade-off-uri sincere

Nimic nu e gratis în web dev, mai ales când te joci cu mecanismele interne din Next.js:

  • Mentenanță: Ne legăm de un API intern (LayoutRouterContext din next/dist). Dacă echipa de la Vercel schimbă radical routerul într-un minor release, codul ăsta s-ar putea să aibă nevoie de update-uri.
  • Performanță și SSR: Tot ce pui sub AnimatePresence la nivel de layout va deveni, practic, client-side friendly. Pierzi parțial din beneficiile de streaming pur pe paginile respective.
  • Scroll position: Câteodată Next.js va încerca să sară la începutul paginii (scroll to top) înainte ca animația de exit să se termine, ceea ce poate crea un layout shift urât. Trebuie să dezactivezi scroll-ul automat din next/link în anumite scenarii.

Pentru proiecte simple de prezentare sau portofolii, merită 100% efortul. Pentru dashboard-uri complexe cu tabele mari, s-ar putea ca lag-ul introdus de animații să enerveze utilizatorii mai mult decât să-i încânte.

Voi cum ați rezolvat tranzițiile de pagină în App Router? Ați rămas pe CSS transitions simple sau ați riscat cu hack-uri de context?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.