07 — Layouts and Templates — Shared UI That Does (or Doesn't) Persist
Two App Router conventions that look almost identical turned out to differ in exactly one behavior — and that behavior is the whole decision. The model that clicked: both wrap child routes, but layouts persist across navigation while templates create a fresh instance on every navigation [1][2]. That single difference — persist vs. reset — is the entire decision between them.
Layouts — the persistent wrapper
A layout.tsx wraps the pages (and nested layouts) beneath it. The key behavior is persistence: when the user navigates between routes that share a layout, the layout component does not remount. Its state survives, its effects don't re-run, and its DOM stays put [1].
// app/layout.tsx — root layout, persists across every navigation
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<nav>Always-here navigation</nav>
{children}
</body>
</html>
);
}The root layout is mandatory and persists for the entire app — that's where the <html> and <body> tags live. Nested layouts persist within their subtree: a dashboard/layout.tsx wraps every page under /dashboard/* and doesn't remount when moving between dashboard pages.
The payoff is the things I don't want to lose on navigation: a sidebar's scroll position, a video player's playback, a tab's open state, a complex form's partial input. Because the layout's state isn't reset, those survive.
Templates — the resetting wrapper
A template.tsx is structurally similar — it wraps each child layout or page — but the behavior is the opposite [3]. On every navigation, a template creates a new instance of its children: the component remounts, DOM elements are recreated, useState resets, and effects re-synchronize.
// app/template.tsx — new instance on every navigation
export default function Template({ children }: { children: React.ReactNode }) {
return <div className="page-transition">{children}</div>;
}When templates are useful is the question worth asking. The roadmap's framing: templates are for when I want the reset [3] — animation entry points that should replay on every navigation, effects that must re-run per page view (analytics pageviews, for instance), or cases where stale state is a bug rather than a feature.
The default is layout
For the vast majority of shared UI — site chrome, persistent navigation, authentication context providers — layout is the right choice, because I don't want a sidebar's scroll position reset every time I click a link. Templates are the deliberate exception, reached for when "fresh on every navigation" is the desired behavior.
How I use this
I default to layout.tsx for everything that wraps multiple routes. The only times I reach for template.tsx are when I'm building a per-page-view animation that must replay, or wiring an analytics call that has to fire on every navigation regardless of caching. The question I ask: should this state survive a navigation? Yes → layout. No → template.
References
[1] Vercel, "Layouts (App Router)," Next.js Docs, 2024. [Online]. Available: https://nextjs.org/docs/app/api-reference/file-conventions/layout#root-layouts
[2] Vercel, "Layouts (Pages Router)," Next.js Docs, 2024. [Online]. Available: https://nextjs.org/docs/pages/building-your-application/routing/pages-and-layouts
[3] Vercel, "Templates," Next.js Docs, 2024. [Online]. Available: https://nextjs.org/docs/app/api-reference/file-conventions/template
[4] D. Pierce, "A guide to Next.js layouts and nested layouts," LogRocket Blog, 2023. [Online]. Available: https://blog.logrocket.com/guide-next-js-layouts-nested-layouts/
[5] "Next.js 15 tutorial — Layouts," YouTube, 2024. [Video]. Available: https://www.youtube.com/watch?v=NK-8a8EzWrU
[6] "Next.js 15 tutorial — Templates," YouTube, 2024. [Video]. Available: https://www.youtube.com/watch?v=yfww2kplO-k
Knowledge check · Question 1 of 4
When the user navigates between two routes sharing a layout.tsx, what happens to the layout?
Comments
Leave a Comment
You must be signed in to comment
0 Comments
No comments yet. Be the first to comment!