You asked your AI coding agent for a counter, a like button, or a theme toggle in a Next.js App Router project. It wrote a clean component with useState and an onClick. You run it and the build or the dev overlay stops with an error about using a React hook in a Server Component. The component looks correct, and it would be correct in a plain React app.
The bug is one missing line at the top of the file, and agents leave it out more often than you would expect.
Why this happens
In the Next.js App Router, layouts and pages are Server Components by default. A Server Component renders on the server and sends the result to the browser. It never runs in the browser, so anything that needs the browser cannot work in it.
The Next.js docs list what needs a Client Component:
- State and event handlers, such as
useState,onClickandonChange - Lifecycle logic such as
useEffect - Browser-only APIs such as
localStorageandwindow - Custom hooks that use any of the above
Agents trained on years of plain React code write these components without the marker, because in a plain React app every component is a client component and nothing needs marking. The code is right for that world, and the App Router is a different one.
How to tell if this is your problem
- Read the error. The Next.js docs describe the cause as using a React client hook in a Server Component. The message names the hook and the file.
- Open that file and check the first line. If it uses
useState,useEffect,useRef, or an event handler likeonClick, and the first line is not'use client', that is the whole problem. - Check where the file lives. A component inside the
app/directory with no directive is a Server Component.
The fix
Add the 'use client' directive at the very top of the file, above your imports. This is the exact pattern from the Next.js error page:
'use client'
import { useState } from 'react'
export default function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>{count} likes</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
)
}
The directive declares a boundary between the server and client module graphs. Once a file has it, everything that file imports and the components it renders directly go into the client bundle. You do not need to repeat it in every child.
Do not just mark the whole page
The quick way to make the error vanish is to put 'use client' on page.js or the root layout. That works, but it pulls the whole tree into the client bundle and gives up what Server Components are for: fetching data close to the source, keeping secrets such as API keys on the server, and sending less JavaScript to the browser.
The docs recommend the narrower move: add the directive only to the interactive piece. Keep the page a Server Component, and import a small LikeButton or Search component that carries 'use client'.
If a Client Component needs to wrap server-rendered content, pass that content in as children. The docs call this interleaving: a Server Component passed as a prop is rendered on the server ahead of time and handed to the Client Component as output.
Two cases agents often hit
- Context providers. React context is not supported in Server Components. Put
createContextand the provider in a file with'use client'that acceptschildren, then use it in your layout. The docs suggest wrapping{children}rather than the whole<html>document. - Third-party components. A package component that uses
useStatebut ships without the directive fails when a Server Component renders it. Wrap it in your own file that starts with'use client'and re-exports it.
How to avoid this next time
When you prompt an agent for UI in a Next.js project, add one line: “this is the App Router, so add 'use client' only to components that use state, effects, event handlers or browser APIs, and keep pages as Server Components.” In review, search for useState, useEffect and onClick and check that each file either starts with the directive or is small enough that the directive is cheap.
Comments
Sign in to join the conversation.
No comments yet — be the first.