skip to content

Routing and Layouts

In the App Router the filesystem is the routing config: where a file sits decides the URL, the layout nesting, and the loading UI. Interviewers start here because caching, rendering, and streaming all hang off the route tree you describe.

part ofNext.jsoverview, primer and where to startread it →
on this pageshow

explore

questions

30

In a Next.js App Router project you create app/blog/[slug]/page.tsx. Which URLs does that one file serve, and how does the page component read the slug value?

level: juniorimportance: must knowfreq 85%

answer

  1. square brackets in the folder name
  2. one segment, any value
  3. keyed by the folder name
  4. always a string, never a number
  5. params is awaited in Next 15+

basics

~20 s

app/blog/[slug]/page.tsx serves every single-segment URL under /blog, such as /blog/hello and /blog/42. Next passes the matched text to the page in the params prop as { slug: 'hello' }; since Next 15 params is a Promise, so await it.

solid answer

~40 s

A square-bracket folder is a **dynamic segment**: `app/blog/[slug]/page.tsx` matches `/blog/hello-world`, `/blog/why-next`, `/blog/42` — one segment, any value, one file. The matched text arrives on the page in the `params` prop, keyed by the folder name, so `params.slug` is `'hello-world'`. Since Next 15, `params` is a Promise, so a server page writes `const { slug } = await params`, and a client page unwraps it with React's `use()`. The value is always a string: `[id]` gives you `'42'`, never the number `42`, so convert and validate before using it in a query. A dynamic segment matches exactly one segment — `/blog/2024/recap` and bare `/blog` are not matched by `[slug]`. Nest more bracketed folders for more values, and export `generateStaticParams` when you want the known URLs prerendered at build time.

go deeper

for a junior

Know that a bracketed folder name matches any single URL segment and that the value arrives in the page's params prop under that exact name. Say plainly that the value is a string.

for a middle

Explain that params is a Promise in Next 15 and later and how each rendering context reads it — await in a Server Component, use() in a Client Component — and where params stops and searchParams begins.

for a senior

Show that a segment value is unvalidated user input: describe how you parse and reject bad values before they reach a query, and how the route's prerendering story changes once generateStaticParams enumerates the values.

for a principal

Own the URL shape itself. Be ready to argue when an identifier belongs in the path versus the query string, what that choice costs in cacheability and shareability, and how slug stability affects links and analytics over years.

## What a dynamic segment is In the App Router, folders under `app/` map to URL segments and a `page` file makes a segment publicly routable. A folder whose name is wrapped in square brackets is a **dynamic segment**: instead of matching one literal path piece, it matches *any* value in that position and hands the matched text to your code. So `app/blog/[slug]/page.tsx` is a single file that serves an unbounded family of URLs: `/blog/hello-world`, `/blog/why-next`, `/blog/42`. The name inside the brackets is not magic beyond one thing — it is the key you read the value under. `[slug]` produces `params.slug`; `[id]` produces `params.id`. ## Where the matched value arrives Next passes the segment values to the route as a prop called `params`. The page for the segment receives it, and so does the `layout` for that segment and `generateMetadata` exported from it. Since Next 15, `params` is a **Promise**, not a plain object. In a Server Component that means an async page: ```tsx export default async function Page({ params }: { params: Promise<{ slug: string }> }) { const { slug } = await params return <h1>{slug}</h1> } ``` In a Client Component page you unwrap the same Promise with React's `use()` hook instead of `await`. The reason for the Promise is that it lets Next start rendering the tree before the request-bound values have to be resolved. ## One segment means exactly one This is the most common misreading. `[slug]` matches one path segment, no more and no less: - `/blog/hello` → matches, `slug` is `'hello'` - `/blog/2024/recap` → does **not** match; that is two segments - `/blog` → does **not** match; there is no value for the segment If you need a variable number of segments you reach for a catch-all (`[...slug]`) or an optional catch-all (`[[...slug]]`) instead. If you need a fixed number, nest folders. ## Several dynamic segments compose `app/shop/[category]/[id]/page.tsx` serves `/shop/shoes/42`, and `params` resolves to `{ category: 'shoes', id: '42' }`. Every bracketed folder on the matched path contributes exactly one key, named after the folder. Names must be distinct along a path — two segments both called `[id]` would collide on the same key. ## params is the path; searchParams is the query string `params` carries **path** segments only. Everything after `?` — `/blog/hello?ref=twitter` — is not in `params`; it arrives in the separate `searchParams` prop, which only `page` receives. Candidates who try to read `params.ref` for a query parameter are showing they have not internalised the split. ## Values are always strings A URL is text, so Next does not coerce types. `/products/42` gives `params.id === '42'`. Two consequences worth stating in an interview: ```tsx const { id } = await params const numericId = Number.parseInt(id, 10) if (!Number.isInteger(numericId)) { // reject rather than passing an arbitrary string into a data query } ``` First, convert deliberately. Second, treat the value as untrusted user input: it is whatever the visitor typed in the address bar, so validate it before it reaches a database call or a filesystem path. ## What a dynamic segment does not give you It does not validate anything — any value matches, and it is your job to decide what a legal one looks like. And it does not, by itself, prerender anything: because Next cannot enumerate the possible URLs, a dynamic route is rendered when a request arrives, unless you export `generateStaticParams` to tell Next which concrete values to build ahead of time. ## The mental model to say out loud "A bracketed folder is a parameter in the URL. It matches one segment, it hands me a string under that name, and I get it from the `params` prop, which I have to await in current Next." That sentence, plus the string-not-number caveat and the params/searchParams split, is a complete junior-level answer.

  • For the route app/shop/[category]/[id]/page.tsx and the URL /shop/shoes/42, what does the resolved params object look like?
    `{ category: 'shoes', id: '42' }`. Every bracketed folder on the matched path contributes one key, named after the folder, and both values are strings — `id` is `'42'`, not `42`. Names have to be distinct along a path, otherwise the two segments would fight over the same key.
  • Does the layout.tsx inside a dynamic segment receive params as well?
    Yes. The `layout` for that segment gets the same `params` — also a Promise in Next 15 and later — as does `generateMetadata` exported from the segment. What a layout does not receive is `searchParams`; only `page` gets the query string.
  • How does a Client Component page read params if it cannot use await?
    It unwraps the Promise with React's `use()` hook: `const { slug } = use(params)`. A Client Component cannot be an async function, so `use()` is the supported way to read the same value. Most teams instead keep the page a Server Component, read `params` there, and pass the plain string down as a prop.

saying these in an interview costs you the question

  • Thinks [slug] also matches /blog/2024/recap
  • Uses params.id as a number without converting it
  • Reads params synchronously in Next 15 without awaiting
  • Looks for ?ref=twitter inside params instead of searchParams
  • Treats the segment value as trusted, safe input

context

open as a page

In a Next.js App Router project you create the folder app/dashboard/settings/ and put a Settings.tsx component inside it, but visiting /dashboard/settings returns a 404. Why, and what actually makes a folder under app/ a routable URL?

level: juniorimportance: must knowfreq 80%

basics

~20 s

In the Next.js App Router, folders only define URL segments. A segment becomes reachable only when it contains a page file that default-exports a component (or a route file for an API endpoint). Every other file in the folder is just colocated code.

open as a page

In the Next.js App Router, what does a folder wrapped in parentheses — for example app/(marketing)/about/page.tsx — do to the resulting URL, and why would you create one?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A parenthesised folder is a route group: Next omits it from the URL, so app/(marketing)/about/page.tsx still serves /about. Groups let you organise route files and scope a layout to a subset of routes without adding a URL segment.

open as a page

In the Next.js App Router, what is special about the root layout at app/layout.tsx compared with a nested layout further down the tree?

level: juniorimportance: must knowfreq 68%

basics

~20 s

The root layout is required, wraps every route in the app, and is the only layout that renders the <html> and <body> tags. Nested layouts render plain markup inside it and apply only to their own segment and below.

open as a page

In the Next.js App Router, what is the difference between a [...slug] catch-all segment and a [[...slug]] optional catch-all, and what does params.slug hold in each case?

level: middleimportance: must knowfreq 68%

basics

~20 s

A [...slug] catch-all matches one or more path segments and gives params.slug as an array of strings. A [[...slug]] optional catch-all matches the same URLs plus the bare parent path, where params.slug is undefined rather than an empty array.

open as a page

In a Next.js App Router app with app/blog/[slug]/page.tsx, what does exporting generateStaticParams from that file do, and what shape must it return?

level: middleimportance: must knowfreq 62%

basics

~20 s

generateStaticParams tells Next which concrete values of a dynamic segment to prerender at build time. It returns an array with one object per route — for [slug], entries like { slug: 'hello' } — with keys named after the bracketed folders and string values.

open as a page

In the Next.js App Router, what does adding an error.tsx file to a route segment do, why must that file start with 'use client', and what does Next pass to it as props?

level: middleimportance: must knowfreq 62%

basics

~20 s

An error.tsx file wraps its route segment's page and everything nested below it in an error boundary. It must be a client module because recovery happens in the browser, and Next renders it with two props: the error and a reset function that retries the segment.

open as a page

In the Next.js App Router, what does a folder named @analytics inside a route segment create, how does its content reach the screen, and what does it do to the URL?

level: middleimportance: must knowfreq 52%

basics

~20 s

An @-prefixed folder is a parallel route slot. Next renders it independently within the same segment and passes it to that segment's layout as a prop named after the slot, so the layout must render it. Slot names never appear in the URL.

open as a page

In the Next.js App Router, what is the difference between a segment's layout.tsx and its template.tsx, and how does that difference show up when the user navigates between two pages under that segment?

level: middleimportance: must knowfreq 78%

basics

~20 s

A layout persists across navigation: its instance stays mounted, state and effects survive, only the child page swaps. A template wraps children the same way but is remounted on every navigation, so state resets, effects re-run, and DOM nodes are recreated.

open as a page

In a Next.js App Router Client Component, what does `router.refresh()` from `useRouter` (next/navigation) actually do, and how does it differ from `window.location.reload()`?

level: middleimportance: must knowfreq 60%

basics

~20 s

router.refresh() re-requests the current route from the server, re-renders its Server Components, and merges the new payload into the live page — keeping client state and scroll position. window.location.reload() throws the whole document away and starts over.

open as a page

In a Next.js project migrating a pages/ route that used getServerSideProps, what replaces it in the App Router, and what changes about where the data is fetched?

level: middleimportance: must knowfreq 72%

basics

~20 s

The App Router has no one-for-one replacement: the page component itself becomes an async Server Component that awaits its data inline. There is no exported data function and no props payload, and per-request freshness becomes something you opt into.

open as a page

Can a single Next.js project contain both a pages/ directory and an app/ directory at the same time, and what happens if the two define the same URL path?

level: juniorimportance: should knowfreq 54%

basics

~20 s

Yes — Next.js supports both directories in one project so teams can migrate route by route. The App Router takes priority, but two routes that resolve to the same URL are rejected at build time with a conflict error rather than one silently winning.

open as a page

A Next.js App Router page needs the ?sort=price query string. Which file receives the searchParams prop, why does a layout.tsx not get one, and what is the resolved value's type?

level: middleimportance: should knowfreq 48%

basics

~20 s

Only page.tsx receives the searchParams prop — a Promise in Next 15 and later that resolves to the parsed query string. Layouts do not: they are preserved and not re-rendered when only the query changes, so a searchParams prop there would go stale.

open as a page

A Next.js App Router project has app/blog/not-found.tsx, but visiting /blog/does-not-exist shows the application's root 404 page instead of that file. Why, and when does a nested not-found.tsx actually render?

level: middleimportance: should knowfreq 42%

basics

~20 s

A URL that matches no route at all is always handled by the root not-found file, because routing never reaches the blog segment. A nested not-found.tsx renders only when the not-found helper is thrown while that subtree is being rendered — typically by a page that looked up a missing record.

open as a page

In the Next.js App Router, what do the (.), (..) and (...) folder prefixes do, and how do they combine with a slot to show a photo as a modal over a feed while /photos/12 still loads as a full page?

level: middleimportance: should knowfreq 42%

basics

~20 s

They are intercepting-route prefixes: (.) matches a route at the same segment level, (..) one level above, (...) from the app root. Placed inside a @modal slot, an intercepted copy of a route renders as an overlay on client navigation, while a direct load still renders the real page.

open as a page

In the Next.js App Router, a layout.tsx exports a metadata object with a title and an openGraph block, and the page.tsx nested below it exports metadata with only a title. What ends up in the rendered head?

level: middleimportance: should knowfreq 48%

basics

~20 s

Next merges metadata down the segment tree shallowly. The page's title wins, and the layout's openGraph block survives untouched only because the page did not define one — had the page exported its own openGraph, it would replace the parent's object entirely rather than merge field by field.

open as a page

In a Next.js App Router Client Component, how do you read the current pathname and query string, and why can a build fail with "useSearchParams() should be wrapped in a suspense boundary"?

level: middleimportance: should knowfreq 55%

basics

~20 s

Use usePathname() and useSearchParams() from next/navigation inside a client component. useSearchParams reads a value known only at request time, so on a statically prerendered route Next requires a Suspense boundary around the component that calls it.

open as a page

In a Next.js app moving from the Pages Router to the App Router, what happens to pages/_app.tsx and pages/_document.tsx?

level: middleimportance: should knowfreq 58%

basics

~20 s

Both collapse into the root app/layout.tsx. That single file renders the <html> and <body> tags _document owned and holds the global CSS imports and shared providers _app owned. The app/ directory has no _app or _document equivalent.

open as a page

A Next.js catalog serves 50,000 products from app/products/[id]/page.tsx, but generateStaticParams returns only the 500 most popular ids. What does a visitor get when they request one of the other 49,500, and what changes if the segment also exports dynamicParams = false?

level: seniorimportance: should knowfreq 45%

basics

~20 s

With the default dynamicParams = true, an id missing from generateStaticParams is still served: Next renders that page on demand at request time. Exporting dynamicParams = false instead makes any unlisted id return the not-found response.

open as a page

In production, a Next.js App Router app renders the segment's error.tsx but with a generic message and no details, while a separate failure replaces the entire UI with Next's default error page instead of hitting any error.tsx. Explain both behaviours and what you would change.

level: seniorimportance: should knowfreq 40%

basics

~20 s

Errors thrown during server rendering are stripped of their details before reaching the browser and arrive with only a digest identifier, so the client-rendered error file cannot show the real message. The whole-UI replacement means the failure happened above every segment boundary — in the root layout — which only a global error file can catch.

open as a page

A Next.js App Router dashboard has app/dashboard/layout.tsx with a @team slot. Navigating to /dashboard/settings by clicking a link inside the app works, but opening or reloading that same URL directly returns a 404. What causes this, and how do you fix it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The @team slot has no route matching /dashboard/settings. On a client navigation Next keeps showing the slot's previous content, but on a full page load there is no previous state, so it falls back to the slot's default.tsx — and with no default.tsx it renders a 404. Add app/dashboard/@team/default.tsx.

open as a page

A Next.js App Router dashboard layout fetches an unread-messages count and renders it in the sidebar. Users report the number is right on a hard refresh but never changes as they click between pages under that dashboard. Why, and what are your options?

level: seniorimportance: should knowfreq 42%

basics

~20 s

Shared layouts are preserved across client navigation: Next re-renders only the segments that changed, so a layout above the changed page never re-runs and the value it fetched on first load stays frozen until a full load or an explicit refresh.

open as a page

A Next.js App Router page calls `redirect('/login')` from next/navigation inside a try/catch, and the redirect silently stops working. What is the mechanism behind `redirect()` and `notFound()`, and what is the fix?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Both functions work by throwing a special control-flow error that Next catches above your code. A surrounding try/catch swallows it, so nothing happens. Call them outside the try block, or re-throw the framework's error instead of absorbing it.

open as a page

In a Next.js App Router app, a filter panel calls `router.push('?sort=price')` on every change and the page jumps back to the top each time. Why does that happen, and how do you keep the scroll position?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The App Router scrolls to the top on a new navigation by default, and changing the query string counts as one. Pass the scroll option — router.push(url, { scroll: false }) — or use scroll={false} on a Link, to keep the position.

open as a page

Your team is migrating a large Next.js app from the Pages Router to the App Router route by route, with both directories live in production. What degrades while the app is half-migrated, and how do you sequence the work to limit it?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Every crossing between the two routers is a full page load that destroys client state, and the app shell is duplicated on both sides. Sequence by linked clusters of routes rather than by file difficulty, so the boundary falls where users rarely cross it.

open as a page

You own a large, stable Next.js application built entirely on the Pages Router. How do you decide whether migrating to the App Router is worth doing, and what would make you decide against it?

level: principalimportance: should knowfreq 33%

basics

~20 s

Decide on where the framework's new capability lands and whether your app can use it. The Pages Router is still supported, so migration is an investment, not a deadline — argue it from concrete wins like server-side data access and smaller client bundles, and decline when the app is in maintenance or its architecture cannot benefit.

open as a page

In the Next.js App Router, what does prefixing a folder inside app/ with an underscore — for example app/_components/ — do, and how would you serve a URL segment that genuinely starts with an underscore?

level: middleimportance: nice to knowfreq 28%

basics

~20 s

An underscore marks a private folder: Next excludes it and everything nested inside it from routing, so even a page file within it produces no URL. To serve a URL segment that really starts with an underscore, name the folder with the URL-encoded form %5F instead.

open as a page

In a Next.js App Router codebase, when is it worth implementing a modal as an intercepting route inside a @modal slot rather than as component state, and what does that choice cost the team?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

Route the modal when its content is a real, shareable resource that deserves a URL, back-button dismissal and server-side data fetching. Keep it in component state when it is transient UI. The routed version costs extra files, a standalone page you must keep working, and fallback boilerplate.

open as a page