Can Server Components Use Hooks in Next.js?

No, most React hooks cannot run in a Server Component. Here is why useState, useEffect, and useContext fail there, and the narrow exceptions that actually work.

6 min read

React hooks in Server Components mostly do not work. A Server Component cannot use useState, useEffect, useReducer, or useContext, because these hooks only work in Client Components, and they depend on a component instance that stays alive in the browser across renders.

A Server Component renders once on the server to produce output, then it is done. There is nothing left to hold state, run an effect after a re-render, or subscribe to a context update, so React refuses to let these hooks run there at all.

App.tsxApp.tsx
// app/dashboard/page.tsx
import { useState } from 'react'
 
export default function DashboardPage() {
  const [open, setOpen] = useState(false)
  return <button onClick={() => setOpen(!open)}>{open ? 'Close' : 'Open'}</button>
}

This page has no "use client" directive, so it is a Server Component by default. Calling the hook inside it fails the build instead of quietly doing nothing, because Next.js detects it while tracing the module graph on the server before any code ever reaches the browser.

Why this fails

Client-side hooks are built around React's render and commit cycle in the browser. State needs a stored value that survives between renders, an effect needs a commit phase to run after, and context needs a live subscription to a Provider that can push new values later.

A Server Component skips all of that. It runs once during the server render, streams its output, and the function itself never runs again for that request, so there is no later render for a stored value or an effect to attach to. React Server Components exist to produce output once and hand it off, not to sit in memory waiting for a future update. That single-pass model is exactly what makes them cheap to run and safe to hold server-only secrets, but it also means none of the hooks built around repeated renders have anything to attach to.

The visible result

Running the dashboard page above produces a build-time error from Next.js, not a silent failure or a blank button. The build stops before the app ever serves that route.

texttext
Error: useState only works in Client Components. Add the "use client" directive at the top of the file to use it.
 
Read more: https://nextjs.org/docs/messages/react-client-hook-in-server-component

The message names the exact hook and points to the fix directly. This is a build-time check, so the app will not start or deploy until the file is corrected, which is safer than shipping a button that silently does nothing when a real visitor clicks it.

The fix

Add "use client" to the top of the file that actually calls the hook, above every import.

App.tsxApp.tsx
// app/dashboard/page.tsx
'use client'
 
import { useState } from 'react'
 
export default function DashboardPage() {
  const [open, setOpen] = useState(false)
  return <button onClick={() => setOpen(!open)}>{open ? 'Close' : 'Open'}</button>
}

The directive is needed here because the component now holds state and responds to a click, and both require a Client Component. This file still renders once on the server to produce the initial HTML, then React hydrates it in the browser so the click handler becomes active. Runs on both the server, for that first pass, and the browser, for every render after that.

Which hooks need a Client Component

Every hook that depends on state, an effect, a ref to a DOM node, or a live subscription needs a Client Component. This covers the hooks developers reach for most often.

HookWorks in a Server Component
useState, useReducerNo
useEffect, useLayoutEffectNo
useContext (consuming a Provider)No
useRef (DOM refs), useTransition, useOptimisticNo
useActionStateNo

useActionState is easy to misjudge because it wraps a Server Action, but the hook itself still tracks pending and returned state across renders in the browser, so the component calling it needs a client directive even though the action it calls runs on the server.

The narrow exceptions that work on the server

A Server Component can still be an async function and await data directly in its body, without any hook at all. This is a React feature built for Server Components specifically, separate from the rules that govern hooks.

App.tsxApp.tsx
// app/dashboard/page.tsx
import { getMetrics } from '@/lib/data'
 
export default async function DashboardPage() {
  const metrics = await getMetrics()
  return <p>Active users: {metrics.activeUsers}</p>
}

This entire file runs on the server only. The await call reads data during the render itself, so no client-side hook is involved and nothing here ships to the browser.

React's cache() function is the other exception. It is built to run in Server Components, and it dedupes repeated calls to the same function during a single server render so several components can request the same data without triggering duplicate work.

typescripttypescript
// lib/data.ts
import { cache } from 'react'
 
export const getMetrics = cache(async (teamId: string) => {
  const res = await fetch(`https://api.example.com/teams/${teamId}/metrics`)
  return res.json()
})

Two different Server Components can both call getMetrics with the same team id during the same render, and the underlying fetch only runs once. This module has no directive and never runs in the browser, since cache() itself is documented for use in Server Components only.

Common confusion

Developers often assume that because a Server Component can render a context Provider, it can also read a value back out of that same context. Rendering a Provider and consuming its value are different operations, and only reading a value with useContext is a hook that requires a Client Component.

App.tsxApp.tsx
// app/theme-provider.tsx
'use client'
 
import { createContext } from 'react'
 
export const ThemeContext = createContext('light')
 
export default function ThemeProvider({ children }: { children: React.ReactNode }) {
  return <ThemeContext.Provider value="dark">{children}</ThemeContext.Provider>
}

A Server Component can import and render the theme provider around its children with no error, because it is only placing a Client Component in the tree. Any component that needs to read the theme value back out still has to be a Client Component of its own, since the read itself is what needs the hook.

For the broader split between the two component types, see Server Components vs Client Components in Next.js. For the mechanics of the directive itself, see The use client Directive Explained. For the difference between Next.js caching and React's own dedupe function, see Next.js Request Deduplication vs React cache: How They Differ, and for guidance on deciding where the boundary should sit, see When to Add use client and When Not To.

Rune AI

Rune AI

Key Insights

  • Server Components cannot use useState, useEffect, useReducer, useContext, useRef for DOM refs, useTransition, useOptimistic, or useActionState.
  • These hooks need a persistent component instance that re-renders in the browser, which a Server Component never has.
  • Next.js reports this at build time as a React client hook in Server Component error, naming the hook and telling you to add use client.
  • A Server Component can still be an async function and await data directly, which is not a hook.
  • React's cache() function can run in a Server Component to dedupe repeated data requests during one render.
  • A Server Component can render a context Provider if that Provider is itself a Client Component.
  • Fix the error by adding use client to the file where the hook is actually called, not to every parent above it.
RunePowered by Rune AI

Frequently Asked Questions

Can a Server Component use useContext to read a value from a Provider?

No. Consuming context with useContext requires a Client Component, because reading context depends on the same render and commit lifecycle that useState needs. A Server Component can still render a context Provider that is itself a Client Component, and pass values down as props instead.

Is async/await in a Server Component a hook?

No. Making a Server Component an async function and awaiting a fetch call is a React feature for Server Components specifically, not a hook. Hooks follow the rules of hooks and only work in Client Components, while async Server Components are a separate mechanism for data fetching during render.

Does useActionState work in a Server Component?

No. useActionState tracks pending state and a returned value across renders in the browser, so the component calling it must be a Client Component, even though the Server Action it wraps runs on the server.

Why does my error mention useState when I never imported it directly?

The error follows the import chain. If a Server Component imports a helper or a third-party component that itself calls useState without its own use client boundary, Next.js reports the hook back up through the first Server Component that reached it.

Conclusion

Server Components cannot use hooks that depend on state, effects, or a persistent render lifecycle, because a Server Component renders once on the server and never re-renders in place. Reach for a Client Component whenever a piece of UI needs useState, useEffect, useContext, or any hook built on top of them, and keep everything else, including async data fetching, on the server by default.