How does Context work in Server Components?
How this works
- A Provider can be declared on the server
You can create a context and set
valuein a Server Component - every nested Client component will be able to read it viauseContext. - The context value must be serializable
Everything that goes from the server to the client must be safely serialized.
=> Use primitives and simple objects/arrays.
Not allowed: functions, class instances, proxies, DOM nodes, etc.
Allowed: strings, numbers, booleans,
null, plain objects, arrays (and anything you're ready to turn into JSON yourself). - Context flows top-down, not back up Context set on the server is available to the client. But state changed in a client Provider cannot "raise" changes back to the server: server components have already been rendered for the request.
- A client-only Provider cannot be imported from the server
If a Provider uses
useState/useEffecthooks and is marked"use client", you cannot import it directly from a Server Component. Wrap the tree through a client "wrapper" component. - Per-request isolation Server Components render on every request. Context created on the server (for example, from cookies/headers/DB) is automatically scoped to a specific request (great for locale, auth, feature flags).
Mini-example (Next.js / RSC)
shared/context.ts
javascript
import { createContext } from "react";
export type AppCtx = { locale: string; userName?: string | null };
export const AppContext = createContext<AppCtx>({ locale: "en" });app/layout.tsx (Server Component)
javascript
import { cookies } from "next/headers";
import { AppContext } from "@/shared/context";
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const cookieStore = await cookies();
const locale = cookieStore.get("locale")?.value ?? "en";
// the value must be serializable:
const value = { locale, userName: null };
return (
<html lang={locale}>
<body>
<AppContext.Provider value={value}>
{children}
</AppContext.Provider>
</body>
</html>
);
}components/UserGreeting.tsx ("use client")
javascript
"use client";
import { useContext } from "react";
import { AppContext } from "@/shared/context";
export function UserGreeting() {
const { locale, userName } = useContext(AppContext);
return <p>{userName ? `Hi, ${userName}!` : `Locale: ${locale}`}</p>;
}Here the Provider on the server hands out value, and the client component reads it via useContext.
Practical tips
- Split contexts by area (locale/auth/theme) to avoid unnecessary re-renders of client consumers.
- Don't put secrets in context: anything available to the client is considered "public". Keep access logic on the server, and pass only "safe" derived values into the context.
- Memoize large values on the client side (if the Provider is a client one) to avoid triggering unnecessary re-renders (
useMemoforvalue). - For client-side dynamics (theme, toggles) wrap the relevant part of the tree with a client Provider. This won't affect the server components above it.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.