Context and SSR
What React Context does
React Context lets you:
- store "global" data (for example,
user,theme,locale,auth), - pass it deep into the component tree without props.
Example (client-side):
const ThemeContext = createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Page />
</ThemeContext.Provider>
);
}What happens during SSR
During Server-Side Rendering, React:
- builds the React tree on the server,
- renders it with the current context values into HTML,
- sends the HTML to the client,
- the client performs hydration, "reviving" that same context in the browser.
So Context participates in rendering the HTML, and its values determine what the server puts into the HTML (for example, a dark theme or a user's name).
Yes, context works with SSR
Example:
// theme-context.ts
import { createContext, useContext } from "react";
export const ThemeContext = createContext("light");
export const useTheme = () => useContext(ThemeContext);// App.tsx
import { ThemeContext } from "./theme-context";
export function App({ theme }: { theme: string }) {
return (
<ThemeContext.Provider value={theme}>
<Page />
</ThemeContext.Provider>
);
}// server.tsx
import { renderToString } from "react-dom/server";
import { App } from "./App";
app.get("/", (req, res) => {
const html = renderToString(<App theme="dark" />);
res.send(html);
});The server renders the page in the dark theme right away.
During hydration, the client gets the same context (value="dark"), and React doesn't re-render.
But! The context must be unique per request
A context on the server is a global React object, so if you use the same instance for every user, it can "leak" between requests.
Example of unsafe code:
// a shared context for all users - unsafe
const UserContext = createContext(null);
app.get("/", (req, res) => {
UserContext._currentValue = { id: 1, name: "Alice" };
const html = renderToString(<App />);
res.send(html);
});→ If the next user is "Bob", he might see "Alice's" data. This is a race condition between requests.
The right way: "isolate the context per request"
Create the Provider inside the render of each SSR request:
const UserContext = createContext(null);
app.get("/", async (req, res) => {
const user = await getUserFromSession(req.cookies.token);
const html = renderToString(
<UserContext.Provider value={user}>
<App />
</UserContext.Provider>
);
res.send(html);
});This way, every request gets its own context instance and its own data.
How this works in Next.js
In Next.js, all of this is automated:
// layout.tsx
export default function RootLayout({ children }) {
const theme = cookies().get('theme')?.value || 'light';
return (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
);
}- On the server,
cookies()is read on every request. - The context is created uniquely for rendering the page.
- On the client, it hydrates without desynchronization.
What's important to remember
| Feature | Explanation |
|---|---|
| Works with SSR | Context participates in server-side rendering |
| Passed into the HTML | The context value affects the HTML the user sees |
| Cannot be stored globally | Otherwise data "leaks" between users |
| Hydration must match | The context on the client must have the same value as on the server |
| You can use multiple contexts | For example, UserContext, ThemeContext, LocaleContext at the same time |
Conclusion
React Context works great with SSR as long as:
- You create the
Providerinside each SSR request (a unique instance). - The context values match between server and client (otherwise hydration "breaks").
- You don't perform DOM operations inside context effects on the server.
A short recipe for safe SSR + Context
// server.tsx
renderToString(
<AuthProvider user={user}>
<ThemeProvider theme="dark">
<App />
</ThemeProvider>
</AuthProvider>
);- Every request gets its own
Provider - Contexts are passed through props
- The provider tree is identical on the client and the server
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.