Next.js App Router
File-based routing, layouts, and the server/client boundary you must hold in your head.
50 minDifficulty 3/5nextjs · frameworkAI-writtenWritten by a model on 16 August 2026 and not yet read by a person. Checked automatically: schema, the pedagogical rules the hand-written material is held to, and every diagram parsed for real.
Before this
Why this exists
A file at `app/blog/[slug]/page.tsx` becomes the route `/blog/hello-world` with zero routing configuration written anywhere — the file system IS the router. But every file in that tree also has to answer a question traditional client-only React never asked: does this code run on the server, the client, or both?
The mental model
Every component is a Server Component by default — rendered on the server, sending only HTML (plus a description of client components) to the browser, never shipping its own JavaScript. Adding `'use client'` at the top of a file flips everything below it (and everything it imports) into the traditional client-rendered model, with its own JavaScript bundle and access to hooks like `useState`.
How it works
Layouts nest and persist across navigation
A `layout.tsx` file wraps every route beneath it in the folder tree, and — unlike a page — it does NOT re-render or remount when navigating between sibling routes that share it. A sidebar in a layout keeps its scroll position and internal state intact as the main content area changes underneath it.
Special files define behaviour, not just structure
`loading.tsx` in a folder automatically wraps that route segment in a Suspense boundary, showing itself while the segment's data loads. `error.tsx` catches errors thrown during rendering of that segment. `not-found.tsx` renders when `notFound()` is called. These aren't imported or wired up manually — their filename alone is the convention that activates them.
Data fetching happens directly in Server Components, with no separate API layer required
A Server Component can `await fetch(...)` or query a database directly in its body — this code runs only on the server, never ships to the client, and the component simply renders once the data arrives. This eliminates an entire class of client-side loading states for data needed on initial render, since the HTML sent to the browser already contains the data.
The server/client boundary is a tree cut, not a per-component toggle
Once a file has `'use client'`, every component it imports also becomes part of the client bundle, even if those imported components don't themselves need any client-only features — the boundary propagates downward through the import tree. This is why placing `'use client'` as low as possible in the tree (on the specific interactive leaf, not a wrapping layout) keeps more of the tree server-rendered.
The mechanism
A request for a route resolves the matching folder path, wrapping the matched page in every layout above it in the tree. Server Components in that tree fetch their own data and render to HTML on the server. Any 'use client' component embedded within is sent as both server-rendered HTML (for the initial paint) and a JavaScript bundle that hydrates it into an interactive component once loaded in the browser.
flowchart TD L[layout.tsx - persists] --> P[page.tsx - server component] P -->|await fetch/db| D[Data fetched on server] P --> C["'use client' component"] C -->|hydrates in browser| I[Interactive on client]
What people get wrong
- 'use client' means a component only runs on the client and never on the server.
- A client component's initial render still happens on the server too, producing HTML for the first paint — 'use client' means the component ALSO ships its JavaScript to hydrate and become interactive in the browser, not that server-side rendering is skipped entirely. This misunderstanding leads to confusion about why a client component's console.log can appear in both the server terminal (during the initial server render) and the browser console (after hydration).
- Putting 'use client' at the top of your app's root layout is a harmless way to 'just make everything work like before'.
- Because the client boundary propagates down through every import, doing this converts the ENTIRE application into client-rendered components, losing every benefit of Server Components — no more server-only data fetching, larger JavaScript bundles, and no automatic code-splitting at the server/client seam. This is a common instinct when migrating an existing client-rendered app, and it defeats the entire purpose of the App Router's rendering model rather than easing into it.
- layout.tsx behaves just like a wrapping component that re-renders on every navigation, same as a page.
- A layout specifically does NOT re-render when navigating between sibling routes it wraps — its state and scroll position persist across that navigation, which is a deliberate behavioural difference from a page, not an implementation detail to ignore. Relying on a layout re-mounting on every navigation (to reset some state, say) will produce a bug, because that reset simply won't happen the way it would for a page.
When not to use it
- A component needs `useState`, `useEffect`, or any browser-only API like `window` or event listeners.
- `'use client'` on that specific component (or the smallest wrapping component that needs it), keeping everything above and beside it in the tree server-rendered where possible.
- You need data at request time that depends on the specific user's session or cookies.
- Read cookies/headers directly in a Server Component via Next's server-only APIs — no need to route this through a client-side fetch and a separate API endpoint.
Terms
- Server Component
- — A component that renders only on the server, sending HTML to the browser without shipping its own JavaScript bundle — the default in the App Router.
- 'use client'
- — A file-level directive marking a component (and everything it imports) as part of the client-rendered, hydrated JavaScript bundle.
- Layout
- — A file that wraps nested routes and persists across navigation between sibling routes, unlike a page which remounts on navigation.
- Hydration
- — The process of a client component's server-rendered HTML becoming interactive in the browser once its JavaScript bundle loads and attaches event handlers.
In an interview
Why would you put 'use client' on a small button component deep in the tree rather than on a layout near the root?
- the client boundary propagates downward through every import from where it's declared
- declaring it high up (near the root) converts everything beneath it into client components, losing server-rendering benefits for the whole tree
- declaring it on the smallest actual interactive leaf keeps the rest of the tree server-rendered, with a smaller JavaScript bundle
Can you recall it?
What does 'use client' actually change, and why does placing it high up in the component tree affect more than just that one file?