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.
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.
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.
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.
layout.tsx)./settings.By nesting layouts, Next.js reduces the amount of work the browser has to do:
| Feature | layout.tsx | page.tsx |
|---|---|---|
| Persistence | Lives across multiple routes. | Unique to a specific URL. |
| State | Preserved on navigation. | Reset on navigation. |
| Best For | Global Nav, Sidebars, Providers. | Main content, Data-heavy views. |
| Re-renders | Only when its own props change. | Every time the URL changes. |
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 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.
Parallel routes are created using Named Slots. Slots are defined by prefixing a folder with the @ symbol.
app/dashboard/@notifications/page.tsx and app/dashboard/@stats/page.tsx.// 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>
);
}The power of Parallel Routes lies in their independence. Each slot is treated as a separate route segment:
loading.tsx inside @notifications and a different one inside @stats. One section of our dashboard can be Loading... while the other is fully interactive.@stats fetch fails, the @notifications section remains perfectly functional.@notifications/settings) without affecting the state of the other slots.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.
/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.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.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}</>;
}| Feature | Parallel Routes (@slot) | Standard Component Import |
|---|---|---|
| Loading States | Built-in loading.tsx support. | Must manually wrap in <Suspense>. |
| Error Handling | Built-in error.tsx per slot. | Must manually wrap in Error Boundaries. |
| URL Sync | Can have its own URL sub-paths. | UI state is not tied to the URL. |
| Data Fetching | Server-side by default. | Can be either, but less "Next.js native". |
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.