From a628b69133af538e43e3c9fe35900aaf34628053 Mon Sep 17 00:00:00 2001 From: moh Date: Sun, 20 Sep 2026 19:55:13 +0200 Subject: [PATCH] FIXED - Move page transition from template to persistent layout so it animates A template.tsx is re-created on every navigation, resetting AnimatePresence so neither enter nor exit ran. Move the transition into a PageTransition client component rendered in the persistent (site) layout, so AnimatePresence survives navigations and the old page animates out before the new one in. --- app/[locale]/(site)/layout.tsx | 5 +++- .../layout/page-transition.tsx | 25 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) rename app/[locale]/(site)/template.tsx => components/layout/page-transition.tsx (54%) diff --git a/app/[locale]/(site)/layout.tsx b/app/[locale]/(site)/layout.tsx index 1e27e7a..8edc347 100644 --- a/app/[locale]/(site)/layout.tsx +++ b/app/[locale]/(site)/layout.tsx @@ -4,6 +4,7 @@ import { getLocale } from "next-intl/server"; import { redirect } from "next/navigation"; import { SiteAmbientBackdrop } from "@/components/layout/site-ambient-backdrop"; +import { PageTransition } from "@/components/layout/page-transition"; import { SiteDock } from "@/components/layout/site-dock"; import { SiteFooter } from "@/components/layout/site-footer"; import { ScrollSmootherProvider } from "@/components/scroll-smoother-provider"; @@ -47,7 +48,9 @@ export default async function SiteLayout({ children, params }: SiteLayoutProps)
-
{children}
+
+ {children} +
diff --git a/app/[locale]/(site)/template.tsx b/components/layout/page-transition.tsx similarity index 54% rename from app/[locale]/(site)/template.tsx rename to components/layout/page-transition.tsx index c699126..41a28e2 100644 --- a/app/[locale]/(site)/template.tsx +++ b/components/layout/page-transition.tsx @@ -6,15 +6,12 @@ import { usePathname } from "next/navigation"; import { useContext, useState, type ReactNode } from "react"; /** - * App Router swaps in the new page's content as soon as you navigate, so the - * outgoing page would normally vanish before it can animate out. `FrozenRouter` - * pins the layout-router context to the value it had when this instance - * mounted, keeping the *old* subtree rendered while its exit animation plays. + * Pins the layout-router context captured when this instance mounted. Each + * keyed page gets its own FrozenRouter, so the *outgoing* page keeps rendering + * its old route data while it animates out. */ function FrozenRouter({ children }: { children: ReactNode }) { const context = useContext(LayoutRouterContext); - // Freeze the context captured at mount so the outgoing subtree keeps - // rendering (with its old route data) while its exit animation plays. const [frozen] = useState(context); if (!frozen) { @@ -25,21 +22,23 @@ function FrozenRouter({ children }: { children: ReactNode }) { } /** - * One unified page transition on every navigation: the old page animates out - * (blur + rise up) before the new page animates in (blur + rise from below). + * One unified page transition on every navigation. Lives in the (persistent) + * site layout — NOT in a `template.tsx`, which Next re-creates on each + * navigation and would reset AnimatePresence so nothing animates. The old page + * animates out (rise + blur), then the new page animates in from below. */ -export default function SiteTemplate({ children }: { children: ReactNode }) { +export function PageTransition({ children }: { children: ReactNode }) { const reducedMotion = useReducedMotion(); const pathname = usePathname(); return ( - + {children}