Overview
In the App Router, folders under app/ are URL segments and reserved file names define what each segment does: page.tsx makes it routable, layout.tsx wraps children, and loading.tsx, error.tsx, and not-found.tsx cover loading, failure, and 404 states. The router is server-first, built on react-server-components. A folder with no page.tsx produces no route.
Put one folder per segment under app/
app/
layout.tsx // root layout (required)
page.tsx // "/"
posts/[slug]/
page.tsx // "/posts/:slug"
Use [param] for one dynamic segment, [...slug] for a catch-all, and [[...slug]] for an optional catch-all. params and searchParams are Promises; see nextjs-async-dynamic-apis. A route.ts cannot sit beside a page.tsx in the same segment.
Use layouts for shells and pages for content
A layout.tsx wraps every page below it, persists across navigations between its children, and does not re-render on them. Layouts do not receive searchParams. Put nav, providers, theme, and auth context there. The root app/layout.tsx is the only required file and must render <html> and <body>.
Type route props with the generated helpers
Run next typegen (also run by next dev and next build) to get global PageProps<'/blog/[slug]'>, LayoutProps, and RouteContext types, so you do not hand-write Promise<{ ... }> for every page.
export default async function Page(props: PageProps<"/blog/[slug]">) {
const { slug } = await props.params;
return <h1>{slug}</h1>;
}In 16.3, import { lang } from "next/root-params" reads root-level params such as [lang] from any Server Component without prop drilling.
Group routes without changing the URL
Wrap a folder name in parentheses: app/(marketing)/about/page.tsx serves /about. Groups exist to give sections their own layouts, for example a marketing shell and an authenticated shell, not to shape URLs.
Render independent panes with parallel routes
A folder named @feed or @sidebar is passed to the parent layout as a prop with the same name, and each slot has its own loading and error state. As of Next.js 16, every slot needs an explicit default.js; builds fail without it. Return null or call notFound() in default.js to keep the old behavior.
// app/layout.tsx
export default function Layout(
{ children, sidebar }: { children: React.ReactNode; sidebar: React.ReactNode },
) {
return (<><main>{children}</main><aside>{sidebar}</aside></>);
}Intercept routes for modals over a base page
An intercepting route renders another route inside the current layout using (.), (..), or (...) prefixes that mirror relative paths. Combine it with a parallel @modal slot so a thumbnail click opens a modal while a direct visit or refresh renders the full page.
app/photos/[id]/page.tsx // full page
app/@modal/(.)photos/[id]/page.tsx // modal on client navigation
app/@modal/default.tsx // required: return null
Migrate from the Pages Router one route at a time
app/ and pages/ coexist, and a route cannot exist in both.
getServerSidePropsbecomes anasyncserver component that awaits data inline.getStaticPropsandgetStaticPathsbecome a server component withgenerateStaticParams._app.tsxbecomesapp/layout.tsxplus per-segment layouts.pages/api/*becomes [[frontend/nextjs-route-handlers|route.tshandlers]], and form mutations become server actions.