Next.js Server Component Error: Cannot use client-side API in a Server Component
ServerComponentError — client API used in Server Component
Verified against Next.js 14.2 docs: Server and Client Components, Next.js 15.0 docs: 'use client' directive, React RFC: Server Components specification · Updated June 2026
> quick_fix
You used a React hook (useState, useEffect, useRef), an event handler (onClick, onChange), or a browser API (window, document, localStorage) in a Server Component. Add 'use client' at the top of the file to make it a Client Component, or extract only the interactive part into a separate Client Component file.
// Option 1: Add 'use client' to the entire file
'use client';
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}What causes this error
In the Next.js App Router, every component is a Server Component by default. Server Components render on the server and send HTML to the client — they never run in the browser. This means React hooks that manage client-side state (useState, useReducer), side effects (useEffect, useLayoutEffect), refs (useRef for DOM nodes), event handlers (onClick, onSubmit), and browser APIs (window, document, localStorage, navigator) cannot work because there is no browser environment on the server. When Next.js detects these APIs in a Server Component during compilation, it throws an error because the code would fail at runtime if allowed to proceed.
How to fix it
- 01
step 1
Identify what triggered the error
The error message explicitly names the disallowed API. Common triggers: 'useState is not a function', 'useEffect is not allowed in Server Components', 'Event handlers cannot be passed to Client Component props from Server Components', or 'window is not defined'. Read the error message — it tells you exactly which API and which file.
# Typical error messages: # × You're importing a component that needs `useState`. This React hook only works in a Client Component. # × You're importing a component that needs `useEffect`. This React hook only works in a Client Component. # × Event handlers cannot be passed to Client Component props from a Server Component. - 02
step 2
Decide: should this entire file be a Client Component?
If the component is purely interactive (a form, a modal, a counter, a dropdown), add 'use client' at the very top of the file, before any imports. This is the simplest fix. But if the component is mostly static with one small interactive element, the better pattern is to extract just the interactive part.
- 03
step 3
Option A: Add the 'use client' directive
Place 'use client' as the very first line of the file (before imports). This marks the file and everything it imports as Client Components. They will still be server-rendered for the initial HTML, but they will also hydrate and run in the browser.
'use client'; import { useState, useEffect } from 'react'; export function SearchBar() { const [query, setQuery] = useState(''); useEffect(() => { // runs in the browser after hydration document.title = query ? `Search: ${query}` : 'Home'; }, [query]); return ( <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder="Search..." /> ); } - 04
step 4
Option B: Extract the interactive part into a separate Client Component
Keep the parent as a Server Component (for data fetching, direct database access, keeping secrets server-side). Create a new file for just the interactive UI, mark that file with 'use client', and import it into the Server Component.
// app/products/page.tsx — Server Component (no 'use client') import { db } from '@/lib/db'; import { AddToCartButton } from './add-to-cart-button'; export default async function ProductsPage() { const products = await db.product.findMany(); // direct DB access return ( <ul> {products.map((p) => ( <li key={p.id}> {p.name} — ${p.price} <AddToCartButton productId={p.id} /> </li> ))} </ul> ); } // app/products/add-to-cart-button.tsx — Client Component 'use client'; import { useState } from 'react'; export function AddToCartButton({ productId }: { productId: string }) { const [added, setAdded] = useState(false); return ( <button onClick={() => setAdded(true)}> {added ? 'Added ✓' : 'Add to Cart'} </button> ); } - 05
step 5
Fix the window/document access pattern
If you need browser APIs like window or document in a Client Component, guard them with a typeof check or useEffect. Even Client Components server-render their initial HTML, so window is undefined during SSR.
'use client'; import { useEffect, useState } from 'react'; export function WindowWidth() { const [width, setWidth] = useState(0); useEffect(() => { // Safe: useEffect only runs in the browser setWidth(window.innerWidth); const handleResize = () => setWidth(window.innerWidth); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return <span>Width: {width}px</span>; }
How to verify the fix
- npm run build completes without Server Component errors.
- The interactive element works in the browser (click handlers fire, state updates render).
- Server Components still render their content in the initial HTML (view page source to confirm).
Why ServerComponentError happens at the runtime level
The Next.js App Router compiler statically analyzes each file's imports and API usage during bundling. Files without a 'use client' directive are treated as Server Components and compiled for server-only execution. When the compiler encounters an import of useState, useEffect, or other client-only React hooks in a server-targeted file, it throws at compile time. Similarly, references to browser globals like window or document fail because the server-side Node.js runtime does not have a DOM. This is a deliberate compile-time guard — catching the error early prevents cryptic runtime failures in production.
Common debug mistakes for ServerComponentError
- Putting 'use client' below the imports instead of at the very top of the file — the directive must be the first statement, before any import declarations, or it is silently ignored.
- Making every component a Client Component to avoid the error — this defeats the purpose of Server Components and sends unnecessary JavaScript to the browser, hurting performance and eliminating the ability to do direct database queries.
- Passing an onClick handler from a Server Component to a child component's props — functions are not serializable and cannot cross the server-client boundary; the event handler must live inside a Client Component.
- Using typeof window !== 'undefined' checks in a Server Component instead of moving the code to a Client Component — the guard hides the error but the component still ships server-only, meaning the guarded code never runs.
When ServerComponentError signals a deeper problem
This error is the most visible symptom of the mental model shift that Server Components introduce. In the Pages Router, every component ran in both environments — server for SSR, then client for hydration. In the App Router, components run in one environment by default (server), and you opt specific components into the client. Developers who learned React in a client-only world instinctively reach for useState and useEffect because that is how React has worked since 2019. The App Router asks you to unlearn that instinct and think about which parts of your UI actually need interactivity versus which parts are just rendering data.
Editor's take
This is the single most common error developers hit when migrating from the Pages Router to the App Router, and it happens because the mental model flipped. In the old world, every component was a client component that also happened to server-render. In the new world, every component is a server component unless you explicitly say otherwise.
The instinct to slap 'use client' on every file and move on is understandable, but it is the wrong move. Every file you mark as 'use client' adds to the JavaScript bundle the browser must download, parse, and execute. If your entire app is Client Components, you have rebuilt the Pages Router with worse ergonomics. The whole point of Server Components is that data-fetching components, layout shells, and static content can render on the server and send zero JavaScript to the client.
The right architecture is a thin Client Component layer wrapped around a thick Server Component core. Your page fetches data server-side (no API routes needed, no useEffect loading spinners). It renders the static parts directly. Only the truly interactive pieces — forms, dropdowns, modals, counters, drag-and-drop — get the 'use client' treatment. Those pieces receive data as serializable props from their Server Component parents.
The composition pattern is the key technique to internalize: a Server Component renders a Client Component and passes other Server Components as children. The Client Component never imports the Server Component directly — it just renders {children}. This lets you nest server content inside client interactivity without breaking the boundary rules. Once you internalize this pattern, Server Component errors largely disappear from your workflow.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
Can I use async/await in a Client Component?
No. Only Server Components can be async functions. If a Client Component needs data, either pass the data as props from a parent Server Component, use useEffect to fetch on the client, or use a library like SWR or TanStack Query. Marking a component 'use client' and making it async will cause a runtime error.
Does 'use client' mean the component only runs on the client?
No. Client Components are still server-rendered for the initial HTML response — Next.js sends pre-rendered HTML for fast page loads. The 'use client' directive means the component will also hydrate in the browser, enabling interactivity. It does not skip server rendering. Think of it as 'use client too' rather than 'use client only'.
Can a Client Component import a Server Component?
Not directly. A Client Component cannot import and render a Server Component because the client bundle cannot include server-only code. However, a Client Component can accept Server Components as children or props (the composition pattern). The parent Server Component renders the Server Component and passes the result through the Client Component's children prop.