Skip to content
JL

Home

About

Blog

Contact

Shop

Portfolio

Privacy

TOS

Click to navigate

  1. Home
  2. Joshua R. Lehman's Blog
  3. Next.js Layouts, Templates, and Route Groups

Table of contents

  • Share on X
  • Discuss on X

Related Articles

Server and Client Components in Next.js
Next.js
11m
Sep 24, 2026

Server and Client Components in Next.js

Server and Client Components are a design boundary, not a contest between two rendering styles. Learn what crosses the boundary, how imports affect bundles, and how to keep interactive islands focused.

#Next.js#Server Components+4
Next.js App Router Mental Model
Next.js
12m
Sep 17, 2026

Next.js App Router Mental Model

The App Router becomes much easier once you stop treating every component as a browser component. Learn how a route is assembled on the server, where interactivity belongs, and how layouts create durable UI boundaries.

#Next.js#App Router+4
Our top five web development technologies of 2024
Web Development
15m
Jun 28, 2024

Our top five web development technologies of 2024

The web development landscape evolves rapidly, with new technologies emerging while established frameworks adapt to changing needs. Based on our experience building solutions for clients across various industries, these five technologies have proven most valuable for delivering robust, scalable applications in 2024.

#React#Vue.js+8
Ask me anything! 💬

© Joshua R. Lehman

Full Stack Developer

Crafted with passion • Built with modern web technologies

2026 • All rights reserved

Contents

  • Route Structure Is Product Structure
  • Use Layouts for Persistent Shared UI
  • Use Templates When Reset Is the Feature
  • Use Route Groups Without Changing URLs
  • Model a Real Application Shell
  • Avoid Structural Traps
  • Key Takeaways
Next.js

Next.js Layouts, Templates, and Route Groups

October 1, 2026•6 min read
Joshua R. Lehman
Joshua R. Lehman
Author
Centred luminous route tree showing an application frame composing nested route layers
Next.js Layouts, Templates, and Route Groups

The App Router makes route structure visible in the file tree, but its special files carry different navigation semantics. A layout.tsx persists. A template.tsx deliberately remounts. A folder wrapped in parentheses affects organization without becoming part of a URL. Treating all three as visual conveniences produces confusing state and accidental navigation behaviour.

The useful framing is architectural: decide which product shell must survive a transition, which experience should begin fresh, and which routes share an internal concern without sharing a path segment.

Route Structure Is Product Structure

Folders in app/ describe route segments. Pages are leaves; layouts compose around their descendants. That makes the tree a map of product boundaries, not simply a place to put files.

app/
  layout.tsx
  (marketing)/
    layout.tsx
    page.tsx
    pricing/page.tsx
  (product)/
    dashboard/layout.tsx
    dashboard/page.tsx
    dashboard/settings/page.tsx

The parentheses are important. (marketing) and (product) organize code, but neither appears in the URL. /pricing remains /pricing; the group has not made it /marketing/pricing.

Start with user journeys, then let them determine the tree. A shared authenticated application frame is usually a stable layout boundary. An onboarding wizard that must restart local state on every step is a candidate for a template. A marketing site and a product application are often separate route groups.

This choice is easier when you write down the transition you are designing. Ask what should visibly stay put when someone moves from an invoice to a report. If the answer is the workspace navigation, account identity, and the broad page frame, those belong above both leaves. Ask what should restart when someone opens a new compose screen. If the answer is a local editor, autofocus, and animation sequence, that is a reset boundary. The route tree becomes clear once persistence has an explicit owner.

Use Layouts for Persistent Shared UI

Layouts wrap child segments and persist across navigation within their scope. That makes them appropriate for app shells: navigation, a signed-in identity, stable providers, and other UI a user should not experience as disappearing on every click.

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="grid min-h-screen grid-cols-[16rem_1fr]">
      <aside>
        <DashboardNavigation />
      </aside>
      <main>{children}</main>
    </div>
  );
}

When /dashboard changes to /dashboard/settings, the dashboard layout remains in place. That persistence is a feature, but it is also a constraint: state inside a child Client Component can remain alive longer than expected. Put durable state there intentionally; do not use a layout merely because two pages happen to look similar today.

Think about data ownership as carefully as UI ownership. A server layout can load a workspace name or permissions once for the shared frame. It should not eagerly fetch every piece of page-specific data just because it sits above the page. Page-specific data stays at the leaf, where loading and error boundaries can describe its actual failure mode. This keeps a slow report query from delaying an otherwise usable application shell.

Persist What a User Perceives as Stable

Navigation chrome and a long-lived workspace belong in a layout. A page-local search query, unsaved wizard step, or animation entrance usually does not.

Use Templates When Reset Is the Feature

template.tsx looks similar to a layout but receives a unique key during navigation. Its children remount when that segment changes. This is useful when a reset is product behaviour: replaying an entrance animation, resynchronizing an effect, or clearing client state at a route transition.

// app/dashboard/template.tsx
export default function DashboardTemplate({
  children,
}: {
  children: React.ReactNode;
}) {
  return <section className="animate-in fade-in">{children}</section>;
}

Use a template sparingly. A blanket template can make a product feel less continuous and can hide an underlying state-ownership issue. Start with a layout; introduce a template only when remounting is explicitly desired and test the transition with real navigation.

The distinction matters for effects. A Client Component inside a persistent layout does not automatically treat a navigation as a fresh mount. If you need an effect to respond to a segment change, either model that input explicitly or choose a template at the intended segment. Do not rely on unmount cleanup as a navigation mechanism when the component was intentionally placed in a persistent layer.

Trace the route layers

Switch routes to see a simplified, browser-only model of the wrappers that compose a route. It illustrates structure; Next.js performs the actual server navigation.

Route-composition simulator

This is a browser simulation of which App Router layers compose a route.

wrapperRoot layout
wrapperRoute group
wrapperTemplate
active leafPage

/dashboard: The dashboard layout persists while the overview page is the active leaf. The template remounts its children for this segment.

export default function Layout({ children }: Props) {
  return <AppShell>{children}</AppShell>;
}

Use Route Groups Without Changing URLs

Route groups allow an internal taxonomy that does not leak into public addresses. They are useful for splitting a site by team, product area, or experience while retaining friendly URLs.

app/
  (marketing)/about/page.tsx     -> /about
  (shop)/cart/page.tsx           -> /cart

Groups can also opt a subset of routes into a nested layout. Put authenticated billing and account screens in (account) so they share an account shell while checkout remains outside it.

There are two caveats worth treating as design constraints. First, two groups cannot create the same resolved path: (marketing)/about and (company)/about conflict. Second, crossing between different root layouts triggers a full page load. Multiple root layouts are powerful for genuinely separate experiences, but they are not a free styling tool.

Route groups are also useful during a migration. A team can move a cluster of pages into (workspace) without changing inbound links, then add a shared nested layout once the ownership is understood. That is safer than changing public URL structure and visual composition in one large pull request. The URL remains a stable contract while the implementation gains a clearer seam.

Model a Real Application Shell

Here is a practical shape for a product with a public site and a signed-in workspace:

app/
  (public)/
    layout.tsx          # public header and footer
    page.tsx            # /
    resources/page.tsx  # /resources
  (workspace)/
    layout.tsx          # authenticated root shell
    dashboard/
      layout.tsx        # dashboard navigation
      page.tsx          # /dashboard
      reports/page.tsx  # /dashboard/reports
      settings/page.tsx # /dashboard/settings

If public and workspace areas must be separate root layouts, make the full reload between them an intentional trade-off. If they can share a root layout, keep one and use nested layouts for presentation differences. The latter often preserves smoother transitions and simpler metadata.

For a layout that owns data access, keep its server/client boundary narrow. Fetch secure shell data on the server and pass a small serializable view model to client navigation. Avoid marking the entire shell as a Client Component because one button needs state.

Before landing this tree, manually test the transitions that matter: direct-load a deep route, move between sibling pages, move between groups, and use the browser back button. Watch which client state stays alive and which loading UI appears. These are product behaviours, so they deserve a navigation test rather than a file-tree review alone.

Avoid Structural Traps

  • Do not build a global layout that knows every page's special case. Move local chrome down to the nearest shared segment.
  • Do not use a route group to solve an authorization problem. A group is organization; enforce access in server-side code and route-level logic.
  • Do not put URL meaning into a group name. Users and analytics only see the resolved path.
  • Do not add a template to fix stale state without identifying who should own and reset that state.
  • Do not create duplicate routes through different groups; the conflict is architectural, not a naming inconvenience.

Key Takeaways

  • Layouts persist and are for stable shared product UI.
  • Templates remount descendants and are for deliberate reset behaviour.
  • Route groups organize code and layout scope without changing URLs.
  • Multiple root layouts create distinct experiences but make cross-root navigation a full load.
  • Put each wrapper at the smallest route boundary that matches the user journey.

Next, we will make route transitions robust under failure by building loading, error, and not-found states that belong to the same segment boundaries.