Skip to main content

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):

javascript
const ThemeContext = createContext('light'); function App() { return ( <ThemeContext.Provider value="dark"> <Page /> </ThemeContext.Provider> ); }

What happens during SSR

During Server-Side Rendering, React:

  1. builds the React tree on the server,
  2. renders it with the current context values into HTML,
  3. sends the HTML to the client,
  4. 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:

javascript
// theme-context.ts import { createContext, useContext } from "react"; export const ThemeContext = createContext("light"); export const useTheme = () => useContext(ThemeContext);
javascript
// App.tsx import { ThemeContext } from "./theme-context"; export function App({ theme }: { theme: string }) { return ( <ThemeContext.Provider value={theme}> <Page /> </ThemeContext.Provider> ); }
javascript
// 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:

javascript
// 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:

javascript
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:

javascript
// 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

FeatureExplanation
Works with SSRContext participates in server-side rendering
Passed into the HTMLThe context value affects the HTML the user sees
Cannot be stored globallyOtherwise data "leaks" between users
Hydration must matchThe context on the client must have the same value as on the server
You can use multiple contextsFor example, UserContext, ThemeContext, LocaleContext at the same time

Conclusion

React Context works great with SSR as long as:

  1. You create the Provider inside each SSR request (a unique instance).
  2. The context values match between server and client (otherwise hydration "breaks").
  3. You don't perform DOM operations inside context effects on the server.

A short recipe for safe SSR + Context

javascript
// 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.