App Router foundations

Separate Server and Client Components

Keep components on the server by default and introduce a focused client boundary only when an interface needs browser interactivity.

8 minute lesson

~~~

Pages and layouts in the App Router are Server Components by default. They can read server-side data and secrets without shipping their component code to the browser.

Add "use client" at the top of a file when it needs state, event handlers, effects, context, or browser-only APIs. That directive creates a client boundary for the file and its imports. Keep the boundary small: a server page can render a focused interactive child and pass serializable data into it.

On the first request, Next.js uses the server result to produce HTML for a fast initial display and a React Server Component payload for reconciliation. Client Component JavaScript then hydrates the interactive pieces. On later client navigation, the router can use the server payload without reloading the whole document.

The directive marks a module boundary, not merely one component. Modules imported by that file join the client graph, so putting it high in the tree can ship data-formatting libraries and otherwise static UI to the browser. Pass the smallest serializable props across the boundary; functions, database handles, and arbitrary class instances are not ordinary client props.

// app/counter.tsx
'use client'

import { useState } from 'react'

export function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

Create the Counter component and render it from the server home page. Inspect the browser’s JavaScript requests, then move the boundary to the page temporarily and compare the client graph. Remove "use client" from the counter once to read the state-hook error before restoring it.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →