Layout Patterns

One of the most powerful features of the App Router is its ability to handle Persistent UI. In traditional React apps, navigating usually meant unmounting the entire page tree. In Next.js, we can keep the bones of our application (Sidebars, Navbars, Audio Players) alive while only swapping out the content.

Nested Layouts

Layouts are the foundation of state preservation. When we navigate between two routes that share a layout, Next.js only re renders the page segment, leaving the layout completely untouched.

1. The Wrapping Logic

Layouts follow the folder structure. A layout.tsx in a parent folder wraps the layout.tsx and page.tsx of all child folders. This creates a nested hierarchy of React components.

2. State Preservation: The Sticky Sidebar

Because the Layout component does not unmount during navigation, any React state inside it (or browser state like scroll position and video playback) is preserved.

  • Scenario: We have a search input in our Sidebar (layout.tsx).
  • Action: We type Next.js into the sidebar and click a link to /settings.
  • Result: The Main content area changes to Settings, but our search text Next.js remains in the input.

3. Performance Benefits

By nesting layouts, Next.js reduces the amount of work the browser has to do:

  • Minimal Rendering: Only the parts of the UI that change are updated.
  • Data Fetching: If our Layout fetches data (e.g., User Profile), that data isn't re-fetched when we move between child pages.

4. Layout vs. Page: Responsibility

Featurelayout.tsxpage.tsx
PersistenceLives across multiple routes.Unique to a specific URL.
StatePreserved on navigation.Reset on navigation.
Best ForGlobal Nav, Sidebars, Providers.Main content, Data-heavy views.
Re-rendersOnly when its own props change.Every time the URL changes.

📝 The Composition Flow

When we visit /dashboard/analytics, Next.js essentially renders your app like this:

<RootLayout>
  {/* app/layout.tsx */}
  <DashboardLayout>
    {/* app/dashboard/layout.tsx */}
    <AnalyticsPage /> {/* app/dashboard/analytics/page.tsx */}
  </DashboardLayout>
</RootLayout>

Important Note: We cannot pass data from a Layout to its children via props. Since the Layout wraps the Page, the Page is passed as a {children} prop. To share data, we should fetch the data in both (Next.js will automatically deduplicate the request) or use React Context.


Parallel Routes

Parallel Routing allows us to simultaneously or conditionally render one or more pages within the same layout. This is essential for highly complex dashboards or social feeds where different sections of the page need independent navigation, loading states, or error handling.


1. The Slot Convention

Parallel routes are created using Named Slots. Slots are defined by prefixing a folder with the @ symbol.

  • Folder Structure: app/dashboard/@notifications/page.tsx and app/dashboard/@stats/page.tsx.
  • The Layout: These slots are passed as props to the shared layout at the same level.
// app/dashboard/layout.tsx
export default function DashboardLayout({
  children, // This is the default 'page.tsx'
  notifications, // This is @notifications/page.tsx
  stats, // This is @stats/page.tsx
}: {
  children: React.ReactNode;
  notifications: React.ReactNode;
  stats: React.ReactNode;
}) {
  return (
    <div className="flex flex-col">
      <section>{children}</section>
      <div className="grid grid-cols-2">
        <aside>{notifications}</aside>
        <aside>{stats}</aside>
      </div>
    </div>
  );
}

2. Independent Behavior

The power of Parallel Routes lies in their independence. Each slot is treated as a separate route segment:

  • Independent Loading: We can have a loading.tsx inside @notifications and a different one inside @stats. One section of our dashboard can be Loading... while the other is fully interactive.
  • Independent Errors: If the @stats fetch fails, the @notifications section remains perfectly functional.
  • Independent Routing: We can navigate to a specific state in one slot (e.g., @notifications/settings) without affecting the state of the other slots.

3. Sub-navigation & default.tsx

When navigating, Next.js needs to know what to show in a slot if the current URL doesn't match a specific folder inside that slot.

  • The Problem: If we navigate to /dashboard/settings, but our @notifications slot only has a page.tsx for the root, Next.js won't know what to render in the notifications area.
  • The Solution: default.tsx: We provide this file as a fallback. If Next.js cannot match a slot to the current URL, it will render the default.tsx content to preserve the UI.

4. Conditional Routes

Parallel routes are the cleanest way to handle Role-Based Access Control (RBAC) or Feature Flagging at the layout level.

export default function Layout({
  dashboard,
  admin,
}: {
  dashboard: any;
  admin: any;
}) {
  const role = getPageRole(); // hypothetical function
  return <>{role === "ADMIN" ? admin : dashboard}</>;
}

📝 Parallel Routes vs. Component Imports

FeatureParallel Routes (@slot)Standard Component Import
Loading StatesBuilt-in loading.tsx support.Must manually wrap in <Suspense>.
Error HandlingBuilt-in error.tsx per slot.Must manually wrap in Error Boundaries.
URL SyncCan have its own URL sub-paths.UI state is not tied to the URL.
Data FetchingServer-side by default.Can be either, but less "Next.js native".

🛑 Stop and Think

Think of Parallel Routes as Micro-Frontends within a single layout. They allow us to break down a massive, complex page into small, manageable, and independently-loading chunks without the 'all-or-nothing' rendering cost of a single large page.