LEARN · DEBUGGING GUIDE

Next.js Server Component Incorrectly Using Client Hooks

Next.js enforces strict separation between server components and client-only hooks. Misplacing useState or useEffect leads to hard build/runtime failures.

AdvancedNext.js4 min read

What this usually means

This happens when you invoke React hooks like useState, useEffect, or any 'client' functionality in a Next.js server component—one lacking the 'use client' directive at the top. Server components are rendered on the backend and can't manage browser state or side-effects. Next.js statically analyzes for invalid hook usage at build time, but subtle imports, re-exports, or dynamic 'use client' propagation can sneak through, especially in monorepos or shared component libraries.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Search for useState, useEffect, or useContext in the affected file: grep -En 'use(State|Effect|Context)' ./app/components/YourComponent.tsx
  • 2Check if the file lacks a "use client" directive at the very top
  • 3Review imported modules for transitive client hook usage: grep -Ern 'use(State|Effect|Context)' ./app/components/
  • 4Inspect your shared UI libraries (e.g. packages/ui) for 'use client' boundaries leaking into server code
  • 5If using barrel exports, verify that client components aren't re-exported then imported into server code
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchThe actual component file (e.g. app/components/Foo.tsx) for missing 'use client'
  • searchImports in this file—especially any index.ts barrel files
  • searchAny shared npm package code (packages/ui/ or node_modules) referenced
  • searchNext.js build output: .next/build-manifest.json for component type misclassification
  • searchVercel or CI/CD deployment logs for server/client boundary errors
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningCalling useState/useEffect directly in a .tsx file rendered as a server component by default
  • warningImporting a client component into a server component without wrapping or boundary separation
  • warningShared utility or UI components (e.g., Button.tsx) that internally use client hooks without a 'use client' directive
  • warningBarrel exports (index.ts) that unify client and server components, causing type confusion
  • warningThird-party libraries not marked as 'use client' but implicitly using browser-only hooks
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildAdd 'use client' at the top of the .tsx file needing client hooks—first line, no comments before it
  • buildRefactor shared components to move all React hook logic into clearly marked client components
  • buildIsolate client and server exports in index.ts/barrel files, never mixing them
  • buildUse dynamic imports (e.g., next/dynamic) to load client components from server contexts
  • buildAudit third-party/shared packages for hidden hook usage and mark them appropriately
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedRerun 'next build' and confirm the error is gone
  • verifiedDeploy to a preview environment and check for hydration mismatch or server/client boundary errors
  • verifiedConsole log process.browser or typeof window inside the affected component to confirm execution side
  • verifiedCheck the component's rendered HTML in View Source—server components will be static
  • verifiedEnsure 'use client' components no longer cause build, runtime, or hydration errors
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningBlindly adding 'use client' everywhere—it increases bundle size and can break SSR
  • warningMixing client and server component exports in a single barrel index.ts file
  • warningAssuming library code is always 'safe' for server components—always inspect for client hooks
  • warningIgnoring hydration mismatch warnings; they're often the only sign before breakage
  • warningRefactoring server logic into a client component just to silence the error—violates data fetching best practices
( 07 )War story

Hydration Crash from Shared UI Kit in Next.js Monorepo

Staff Frontend EngineerNext.js 13.4, Monorepo, TypeScript, Yarn Workspaces

Timeline

  1. 11:20Merged PR updating <Button /> in packages/ui to add a hover animation using useState
  2. 11:23Next.js build fails in CI: Error: React hook 'useState' cannot be used in a server component
  3. 11:25Developer tries to add 'use client' to app/components/FancyForm.tsx, error persists
  4. 11:30Barrel file in packages/ui/index.ts found to mix server and client exports
  5. 11:33Split exports: Button goes in packages/ui/client.ts, rest remain in index.ts
  6. 11:38FancyForm imports Button from ui/client.ts, build passes
  7. 11:42Production deploy green, no runtime warnings

We'd just added a subtle useState to a shared Button component in our mono-repo's UI package. The PR was reviewed but nobody noticed the hook addition. Within minutes, CI builds started failing on every project using the UI kit, with the opaque 'React hook cannot be used in a server component' error.

I initially tried slapping 'use client' on various app components, but the error kept bouncing between files. Digging deeper, I found our ui/index.ts barrel was exporting both Button (now client-bound) and typography components (server-safe). That meant importing anything from ui/index.ts could drag in client hooks, even into server code.

Splitting the barrel, adding a ui/client.ts file, and documenting strict import boundaries fixed the build. We now run a grep pre-commit for useState/useEffect in shared libs, and enforce a review checklist for hook boundaries.

Root cause

useState was added to a shared UI component re-exported via a 'server' barrel file, leaking client hooks into server code.

The fix

Export all client-hook-using components from a clearly marked client.ts file and update imports to avoid mixing boundaries.

The lesson

Always audit and split client/server component exports—Next.js won't protect you from transitive hook leaks in shared libraries.

( 08 )Why Client Hooks Are Forbidden in Server Components

Server components in Next.js run during SSR and have no access to browser APIs, state, or effects. Hooks like useState, useEffect, or useLayoutEffect rely on a runtime that doesn't exist in Node.js—hence the hard failure.

Next.js enforces this rule to allow maximally static rendering, and to avoid hydration mismatches. Even a single client hook in a server component can silently break data consistency between server and client.

( 09 )Transitive Hook Pollution via Shared Packages

Most real-world breakage comes not from directly adding useState, but from importing a component from another module or package that added a hook internally. Shared UI kits or monorepo packages are notorious for this.

Always run static analysis (e.g., grep or eslint-plugin-react-hooks) across all shared codebases, and treat any third-party package upgrade as suspect if it suddenly introduces new hook dependencies.

( 10 )Managing Component Boundaries at Scale

In medium to large codebases, barrel files or index.ts exports often accidentally cross client/server lines. The fix is to split exports: create client.ts and server.ts barrels, or use directory structure (components/client, components/server).

Document import boundaries clearly and enforce them in code review—don't rely on Next.js alone to catch all violations.

( 11 )Dynamic Imports: When and Why

If you truly need to render a client component from within a server tree, use next/dynamic with { ssr: false } for explicit client-only loading. This does mean the component will only render on the client and not SSR.

Dynamic imports are a workaround, not a cure. Use them sparingly; if most of your logic is client-bound, reconsider your overall component architecture.

Frequently asked questions

Can I use useContext or useReducer in a server component?

No. All React client hooks, including useContext and useReducer, are forbidden in server components. Only basic rendering and data fetching are allowed on the server side.

Is adding 'use client' everywhere a safe fix?

No. Overusing 'use client' disables server-side rendering optimizations and increases client JS bundle size, hurting performance and SEO. Use it only where absolutely necessary.

How do I safely share UI components between client and server?

Extract all client hook usage into leaf client components, and expose pure-presentational or data-only components for server use. Split your exports and document intent.

Why did this error only appear after upgrading Next.js?

Newer Next.js versions strengthen server/client boundaries, and third-party library updates may introduce hooks into components previously considered server-safe.