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}