Fix: React Context is unavailable in Server Components
The Symptom
You migrated to the Next.js App Router (/app directory) and attempted to wrap your root layout.tsx with a global state provider like a ThemeProvider, Redux, or Authentication Context. Instantly, the build crashes with this terminal error:
Error: createContext only works in Client Components. Add the "use client" directive at the top of the file to use it.
The Cure
If you put "use client" at the top of your root layout.tsx, you ruin all the SEO and Server-Side Rendering benefits of Next.js. Instead, you must isolate the Provider into its own client boundary file, and then import it into the server layout.
Step 1: Isolate the Client Provider
Create a separate file that strictly handles the context.
"use client"; // This is the crucial boundary
import { createContext } from 'react';
// You can use createContext, useState, and useEffect safely here
export const ThemeContext = createContext({});
export default function ThemeProvider({ children }: { children: React.ReactNode }) {
return (
<ThemeContext.Provider value={{ theme: 'dark' }}>
{children}
</ThemeContext.Provider>
);
}
Step 2: Wrap the Root Layout
Import your isolated client component into the server layout.
import ThemeProvider from '@/components/ThemeProvider';
export default function RootLayout({ children }: { children: React.ReactNode }) {
// Notice there is NO "use client" here.
// This layout stays a Server Component for maximum SEO and performance.
return (
<html lang="en">
<body>
<ThemeProvider>
{children}
</ThemeProvider>
</body>
</html>
);
}
The Breakdown
In the Next.js App Router, every component is a Server Component by default. Server Components are rendered on the backend (Node/Edge). Because React Context relies on continuous state and interactive re-renders happening inside the user's browser, the server has no way to process it.
If you try to call createContext() inside a Server Component, React instantly throws an error. The "Boundary Pattern" (shown in the cure) solves this by separating the concerns:
-
The
layout.tsxremains a Server Component, allowing it to fetch secure database data, define metadata, and render raw HTML blazingly fast. -
When Next.js hits the
<ThemeProvider>in the tree, it notes the"use client"directive, ships the JavaScript for that specific node to the browser, and lets the browser handle the interactive React Context.
A Note on "children"
You might wonder: "If I wrap everything in a Client Component, doesn't that turn the entire app into a Client Component?"
No. In React, passing components via the {children} prop allows Server Components to bypass the Client boundary. Your page contents passed inside <ThemeProvider> will still be securely rendered on the server.
Loading logs...