When we first migrated a project to the Next.js App Router, we had a folder full of `layout.tsx` files and zero `template.tsx` files. We didn't even know templates existed until we hit a bug where state wasn't resetting between page navigations and spent two hours debugging before someone in the Next.js Discord said 'you probably want a template there'. Embarrassing? Yes. Avoidable? Also yes, if someone had written a clear explanation. So here's that explanation.
The Quick Version (For Those With Deploys to Do)
Layouts persist across navigations. Once mounted, they don't unmount when you navigate between routes that share the same layout. Templates re-mount on every navigation. That's the entire difference, but the implications are massive depending on what you're building.
Layout = stays alive across navigations, preserves state. Template = remounts fresh on every navigation, resets everything.
How Layouts Actually Work
A layout wraps its children but doesn't care when those children change. If you have a sidebar in your layout and you navigate from `/dashboard/overview` to `/dashboard/settings`, the sidebar doesn't remount. The component tree for the layout stays alive. Any state inside the layout — scroll position, open/closed accordions, form inputs — all of that persists.
// app/dashboard/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
// This component mounts ONCE and stays mounted as long as
// you're navigating within /dashboard/*
// It does NOT remount when you go from /dashboard/overview
// to /dashboard/settings
return (
<div className="flex h-screen">
<Sidebar /> {/* State in here persists across navigations */}
<main className="flex-1 overflow-auto">
{children} {/* Only this part swaps */}
</main>
</div>
);
}This is mostly what you want. Persistent navigation, shared headers, sidebars that remember which items are expanded — layouts are perfect for all of this. The App Router specifically designed layouts to be stable so you get that SPA-like feel without having to manage it yourself.
There's also a practical performance benefit: since the layout doesn't remount, you're not re-fetching data or re-running effects that live at the layout level. For things like user profile data or navigation items pulled from a database, this means one fetch for the entire session in that route group.
How Templates Actually Work
Templates look almost identical to layouts in terms of file structure and exports. The difference is in behavior: a template creates a new instance of itself every single time you navigate. Mount, unmount, mount again. Full reset.
// app/dashboard/template.tsx
// Same signature as layout, completely different behavior
export default function DashboardTemplate({
children,
}: {
children: React.ReactNode;
}) {
// This component REMOUNTS on every navigation within /dashboard/*
// Any state here resets. useEffect with [] runs again.
// It's like navigating to a completely new page from React's perspective.
return (
<div>
{children}
</div>
);
}Because templates remount, all `useEffect` hooks run again on each navigation. All state resets. CSS animations that trigger on mount will play again. It's the behavior you'd expect from the old Pages Router, where every page navigation was a full component lifecycle.
When You Actually Need a Template
Most apps can get by with zero templates. But there are specific cases where templates solve problems that layouts can't.
- Page transition animations: If you want a fade-in or slide-in animation to play every time the user navigates to a new page within a section, you need a template. With a layout, the animation would only play once (on initial mount). With a template, it plays on every navigation.
- Resetting forms between routes: If you have a multi-step form spread across different routes and you want the form state to reset when users navigate back and forward, a template does this automatically.
- Re-running analytics or tracking on each navigation: If you have a useEffect that logs a page view to your analytics system, putting it in a layout means it only fires once per layout mount, not per navigation. In a template, it fires on every navigation.
- Feature tours or onboarding overlays: If you want a contextual overlay to appear fresh every time a user visits a particular section, template behavior is what you want.
- Clearing error boundaries: If a child throws an error and you want the error boundary to reset when the user navigates away and back, a template handles this naturally.
The analytics case is the one that catches people most often. We had a dashboard where we tracked which sections users spent time in. The tracking logic was in a layout, which meant it only fired once per session — completely useless data. Moved it to a template, suddenly we had accurate per-navigation tracking. One file rename, completely different behavior.
You Can Use Both: Layouts Wrap Templates
Here's the part that trips people up: layouts and templates can coexist in the same folder. The layout wraps the template, which wraps the page. So you can have persistent shell chrome in a layout while still getting remount behavior for whatever's inside.
// app/dashboard/layout.tsx
// This persists — navigation, sidebar, user menu
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<div className="flex h-screen">
<nav className="w-64 shrink-0">
<DashboardNav />
</nav>
<div className="flex-1">
{children} {/* template.tsx goes here */}
</div>
</div>
);
}
// app/dashboard/template.tsx
// This remounts on every navigation — page transitions, tracking
"use client";
import { useEffect } from "react";
import { usePathname } from "next/navigation";
export default function DashboardTemplate({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
useEffect(() => {
// This fires on every navigation within /dashboard
trackPageView(pathname);
}, [pathname]);
return (
<div className="animate-in fade-in duration-200">
{children}
</div>
);
}This pattern gives you the best of both worlds: the sidebar doesn't flicker or lose scroll position, but you still get fresh animation and tracking on each page visit. The layout handles chrome, the template handles per-navigation behavior.
The Gotchas We Hit
A few things that weren't obvious to us until we broke something in production:
- Templates are always client components if they use state or effects. You can define them as server components but the remounting behavior only really matters when there's client-side state or effects involved. Don't add 'use client' unless you need it.
- Parallel routes with templates get complicated fast. If you're using @modal or other parallel route slots and also using templates, test your navigation flows thoroughly. The interaction between remounting and parallel routes can produce unexpected results.
- Template vs. layout for loading states: Both can have sibling loading.tsx files. The loading state behavior is the same regardless of whether you're using a layout or template — it's controlled by Suspense boundaries, not by the layout/template distinction.
- Don't put data fetching in templates if you can avoid it. Since templates remount on every navigation, any data fetching inside them runs again every time. If you're fetching shared data (like user profile), put that in the layout.
That last one burned us once. We had a user settings panel where some shared preferences were fetched in the template instead of the layout. Every navigation within the settings section triggered a fresh fetch. Users on slow connections noticed the flash. Moving the fetch up to the layout fixed it — one request for the session, same data.
The Decision Framework
When you're creating a new wrapper component for a route group, ask yourself one question: 'Do I want this to reset when the user navigates within this section?' If no — use a layout. If yes — use a template.
More concretely:
- Navigation, sidebars, headers → layout
- User data that's shared across the section → layout
- Persistent UI state (expanded accordions, scroll position) → layout
- Page entry animations → template
- Analytics tracking per navigation → template
- Forms that should reset between routes → template
- Onboarding/tour overlays that should re-trigger → template
If you're unsure, default to layout. Most applications don't need templates at all, and adding unnecessary remounting just means more work for React. Start with layouts everywhere, then reach for templates when you hit a specific problem that requires remounting behavior.
One More Thing: Root Layout Is Special
The root `app/layout.tsx` is required and cannot be replaced with a template. It must exist, it must return an `<html>` and `<body>` tag, and it never remounts (ever, for the lifetime of the tab). This makes sense — you don't want your entire HTML structure unmounting and remounting on every click. But it means you can't rely on root layout remounting for any behavior. Anything at the root level that needs per-navigation behavior needs to be in a template further down the tree.
// app/layout.tsx — required, never remounts
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{/* Providers, toasters, modals — all live here */}
<Toaster />
{children}
</body>
</html>
);
}
// app/template.tsx — optional, remounts on every top-level navigation
// Useful for app-wide page transition animations
export default function RootTemplate({ children }: { children: React.ReactNode }) {
return (
<div className="animate-in fade-in duration-150">
{children}
</div>
);
}You can have both `app/layout.tsx` and `app/template.tsx` at the root level. The layout stays persistent (html, body, providers), while the template handles any per-navigation behavior you want at the app level.
This is actually how we handle global page transitions in some of the peal.dev templates — root layout holds providers and persistent chrome, root template handles the fade-in animation so it plays on every navigation rather than just on initial page load.
The rule of thumb: layouts for structure and persistence, templates for behavior that should repeat. If you never use a template file, you're probably fine. If you're fighting against layout persistence, that's your cue to reach for template.
The App Router documentation does cover this, but it buries the practical examples under a lot of conceptual framing. The short version is: templates exist for a specific set of cases, and if you don't know you need one, you probably don't. But when you do need one — navigations not triggering effects, animations only playing once, forms not resetting — knowing this distinction will save you hours of confused debugging that could otherwise be spent on something more interesting. Or sleeping.
