Using React Context for State Management with Next.js

Context is a feature of React, a popular JavaScript library for building user interfaces, that enables components to share data without passing props down manually at every level of the component tree. This is particularly useful for data that can be considered "global" for a tree of React components, such as user authentication status or theme preferences.

Next.js, on the other hand, is a React framework that offers capabilities like server-side rendering and generating static websites, making it a robust solution for modern web development.

In this guide, we will navigate through the process of using React Context within a Next.js application. We'll discuss utilizing context within Client Components, handling context within Server Components, and rendering third-party context providers within server components.

Table of Contents

Using Context in Client Components

In Next.js, context is fully supported within Client Components. You can use all the context APIs, such as createContextuseContext, and Provider, in your Client Components. Let's take a look at an example of using context in a Client Component:

app/sidebar.js
import { createContext, useContext, useState } from 'react';
const SidebarContext = createContext();
export function Sidebar() {
const [isOpen, setIsOpen] = useState();
return (
<SidebarContext.Provider value={{ isOpen }}>
<SidebarNav />
</SidebarContext.Provider>
);
}
function SidebarNav() {
let { isOpen } = useContext(SidebarContext);
return (
<div>
<p>Home</p>
{isOpen && <Subnav />}
</div>
);
}
Using React context with Client Components.

In this example, we create a context using createContext and provide its value through a Provider. The SidebarNav component consumes the context value using useContext. This allows components deeper in the tree to access the context value without passing it explicitly as props.

You can use the same code in a JavaScript file (app/sidebar.js) if you prefer:

app/sidebar.js
import { createContext, useContext, useState } from 'react';
const SidebarContext = createContext();
export function Sidebar() {
const [isOpen, setIsOpen] = useState();
return (
<SidebarContext.Provider value={{ isOpen }}>
<SidebarNav />
</SidebarContext.Provider>
);
}
function SidebarNav() {
let { isOpen } = useContext(SidebarContext);
return (
<div>
<p>Home</p>
{isOpen && <Subnav />}
</div>
);
}
Using Context in a sidebar component.

Keep in mind that context providers are typically rendered near the root of an application to share global concerns. However, creating a context at the root of your application in Next.js will cause an error in server components. Let's see how to handle this.

Rendering Third-Party Context Providers in Server Components

In Next.js, React Server Components don't support creating or consuming context directly. If you try to create a context in a Server Component, it will result in an error. Similarly, rendering a third-party context provider that doesn't have the "use client" directive will also cause an error in Server Components.

Instead, you can create your own Client Component that wraps the third-party provider. Here's an example:

app/providers.js
'use client';
import { ThemeProvider } from 'acme-theme';
import { AuthProvider } from 'acme-auth';
export function Providers({ children }) {
return (
<ThemeProvider>
<AuthProvider>{children}</AuthProvider>
</ThemeProvider>
);
}
Creating a file for the React context providers.

In this example, we create a Client Component called Providers that wraps the third-party providers, ThemeProvider and AuthProvider. We mark this component as a Client Component by using the "use client" directive.

Now, you can import and render <Providers /> directly within your root layout:

app/layout.js
import { Providers } from './providers';
export default function RootLayout({ children }) {
return (
<html>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Updating the root layout to consume the context providers.

In the RootLayout component, we import and render the Providers component, which wraps the entire content of the layout. This allows all the components and hooks from the third-party libraries to work as expected within your own client components.

By rendering the providers at the root level, all the components throughout your app will be able to consume the context provided by the third-party libraries.

It's worth noting that you should render providers as deep as possible in the component tree. In the example above, the Providers component only wraps {children} instead of the entire <html> document. This helps Next.js optimize the static parts of your server components.

Resources

Congratulations! You now have a solid understanding of how to use React Context within a Next.js application, including utilizing context in Client Components and rendering third-party context providers in Server Components. This powerful feature will allow you to share data efficiently across your components in Next.js applications.

Couldn't find the guide you need?