---
title: "Next.js: App Router"
slug: "nextjs-app-router"
category: "frontend"
tags: ["frontend", "nextjs", "app-router", "routing", "react"]
status: "stable"
last_updated: 2026-08-14
summary: "App Router file-system routing in Next.js 16: layout vs page, route groups, parallel routes, intercepting routes, and the migration path from Pages Router."
related: ["[[frontend/nextjs]]", "[[frontend/react]]", "[[frontend/react-server-components]]", "[[frontend/nextjs-server-actions]]", "[[frontend/nextjs-route-handlers]]", "[[frontend/nextjs-middleware]]", "[[ops/vercel]]"]
---

## Overview

The App Router is the default in Next.js 16. Routes are folders under `app/`, and the file names in each folder describe what that segment does. A folder becomes a URL segment, a `page.tsx` makes it routable, a `layout.tsx` wraps every child below it, and a handful of other reserved filenames cover loading, errors, and not-found states. The router is server-first; [[frontend/react-server-components]] is the rendering model underneath.

## Put pages in `app/`, one folder per segment

Each folder under `app/` is a URL segment. The folder `app/posts/[slug]/page.tsx` serves `/posts/:slug`. Folders without a `page.tsx` do not produce a route; they only contribute layouts or co-located files.

```
app/
  layout.tsx        // root layout (required)
  page.tsx          // "/"
  posts/
    page.tsx        // "/posts"
    [slug]/
      page.tsx      // "/posts/:slug"
```

Use `[param]` for a single dynamic segment, `[...slug]` for a catch-all, and `[[...slug]]` for an optional catch-all. Type `params` in the page signature; Next.js passes them as a Promise that must be awaited, and Next.js 16 removed the synchronous fallback that Next.js 15 still warned on.

## Use `layout.tsx` for shells, `page.tsx` for routes

A `layout.tsx` wraps every page below it and re-renders only when the layout's own data changes. Put the nav, providers, theme, and auth context here. A `page.tsx` is the leaf that owns the route's main content.

```tsx
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className="dashboard">
      <Sidebar />
      <main>{children}</main>
    </div>
  );
}
```

The root `app/layout.tsx` is the only required file in `app/`. It must render `<html>` and `<body>` and is the right place for the `<Metadata>` defaults (see [[frontend/nextjs-metadata-api]]).

## Group routes without changing the URL

Wrap a folder name in parentheses to create a route group: `app/(marketing)/about/page.tsx` serves `/about`, not `/marketing/about`. Groups exist for organizing layouts, not for URLs.

Use groups to give different sections their own layouts:

```
app/
  (marketing)/
    layout.tsx     // marketing chrome
    about/page.tsx
    pricing/page.tsx
  (app)/
    layout.tsx     // authenticated chrome
    dashboard/page.tsx
```

Two segments in different groups can share the same URL prefix without sharing a layout. This replaces most of the layout-juggling that Pages Router needed.

## Render parallel routes with named slots

Parallel routes let a single layout render two independent panes that each have their own loading and error states. Mark a folder with `@`: `app/@feed/page.tsx` and `app/@sidebar/page.tsx` are both passed to the layout as props.

```tsx
// app/layout.tsx
export default function Layout({
  children,
  feed,
  sidebar,
}: {
  children: React.ReactNode;
  feed: React.ReactNode;
  sidebar: React.ReactNode;
}) {
  return (
    <>
      <main>{children}</main>
      <aside>{sidebar}</aside>
      <section>{feed}</section>
    </>
  );
}
```

Parallel routes are the right primitive for dashboards with independent panels and for modals layered over a base page. Pair them with intercepting routes for modal navigation.

## Intercept routes for modals over a base page

An intercepting route renders one route in the position of another. The convention uses `(.)`, `(..)`, or `(...)` prefixes that mirror relative imports. Use them when a user clicks a thumbnail and a photo opens as a modal over the gallery, but a direct visit to the same URL shows the full photo page.

```
app/
  photos/
    page.tsx              // "/photos" gallery
    [id]/page.tsx         // "/photos/:id" full page
  @modal/
    (.)photos/[id]/page.tsx  // modal over "/photos"
```

The intercepted view runs inside the parallel `@modal` slot when the user arrives via client navigation; the canonical page still renders on a hard load or share.

## Migrate from Pages Router incrementally

`app/` and `pages/` can coexist. Move one route at a time and keep the rest on Pages until the migration is done.

- `getServerSideProps` becomes an `async` server component that awaits its data inline.
- `getStaticProps` plus `getStaticPaths` becomes a server component with `generateStaticParams`.
- `_app.tsx` splits into `app/layout.tsx` (providers) and per-segment layouts.
- API routes in `pages/api/` move to `route.ts` handlers under `app/`; see [[frontend/nextjs-route-handlers]].
- Mutations move from API routes to server actions; see [[frontend/nextjs-server-actions]].

Keep the migration on a branch until every route renders correctly, then delete `pages/`.

## Related

- [[frontend/nextjs]]
- [[frontend/react]]
- [[frontend/react-server-components]]
- [[frontend/nextjs-server-actions]]
- [[frontend/nextjs-route-handlers]]
- [[frontend/nextjs-middleware]]
- [[ops/vercel]]
