Using React Context API with NextJS

Subscribe to my newsletter and never miss my upcoming articles

One huge pain point in React (and NextJS) is having to pass props through multi-level components. The bigger the application or project gets, the more painful this becomes. This is because of how React is structured, in a tree like fashion with data only flowing in one direction.

There are multiple ways to solve this with things like

  • React Redux
  • MobX
  • ...and more

One of the newer ways, especially introduced with React Hooks, is the React Context API.

What is the Context API?

It is a way to enable multiple components (or pages in NextJS) to share global variables and data without having to "pass" these as props. Think of it like a global "object" which stores data.

Although the Context API can be used for most cases, it shouldn't be used for everything. A close observation on component re-renders should be taken when using Context API to prevent any compromising leaks or inefficiencies.

That being said, we can easily implement Context API using React Hooks into NextJS.

Let's step through the creation of a simple custom Auth to allow our NextJS app to see the logged in status of a user, and the users details throughout every component and page.

1. Creating a Provider component

This component will provide our defined data to the rest of our app.

import { useState, createContext } from "react";

export const AuthContext = createContext();

const AuthProvider = ({ children }) => {

    const [ loggedIn, setLoggedIn ] = useState(false);
    const [ userDetails, setUserDetails ] = useState([]);

    const login = value => {

        setLoggedIn(true);
        setUserDetails({
            name: "Alan",
            notifications: 3
        });
    }

    const logout = value => {

        setLoggedIn(false);
        setUserDetails([]);
    }

    const contextValue = {

        status: {

            loggedIn,
            login,
            logout
        },
        user: {

            userDetails,
            setUserDetails
        }
    };

    return (
        <AuthContext.Provider value={ contextValue }>
            { children }
        </AuthContext.Provider>
    );
};

export default AuthProvider;
  • The createContext() method, allows us to create a context object to hold some global data.
  • I've set up some state variables using the useState hook, including a variable for logged in, and a variable for the user details.
  • Custom login/logout functions created to set the logged in status and also the user details (for this example, when logging in, we just mimic it with hard-coded values).
  • I've set up a contextValue object which we can use to structure our global data nicely, for example, for the loggedIn variable and functions, I've called it status, which means, further down the React Tree of components we will be able to access the loggedIn state variable like AuthContext.status.loggedIn which we will see shortly.
  • A Provider component will always return the children, in our case, the children will be the component or page, wrapped with our AuthContext.Provider.

We can save this Provider in a providers folder or utils or anything you like.

2. Wrap our app root component with our Context

In traditional React, we would wrap our <App /> with the Context, however, in NextJS, this doesn't exist. Instead we can provide a custom App component by creating a file in the root folder called _app.js, it should already be present in most cases.

Let's first import our AuthProvider component:

import AuthProvider from "../utils/AuthProvider";

Then wrap our <Component .... /> with this Provider. At this point then the Component will be passed as a child to our Context API Provider, which we saw above with the return of children.

return (

    <AuthProvider>
        <Component { ...pageProps } />
    </AuthProvider>
);

3. Import useContext and our custom AuthContext into index.js

In order to use the values from our Provider, we need to utilise the useContext hook from React, as well as importing our AuthContext we created earlier:

import { useContext } from "react";
import { AuthContext } from "../utils/AuthProvider";

Inside our Home (or any other) component/page we can easily grab all of our Context global variables using the above hook, and destruct our custom, nicely formatted values like this:

const { status, user } = useContext(AuthContext);

At this point, we now have access, globally to the variables we set up earlier, e.g. { status.loggedIn } will return the loggedIn value held in that state variable.

4. Setting up a simple login / logout UI

Using our global Context API variables/functions and data, we can now manipulate our UI based on this, let's set up a very basic, mimicked example of this:

Example of our return in index.js

<main className={styles.main}>
    <h1 className={styles.title}>
        Welcome to <a href="https://nextjs.org">Next.js!</a>
    </h1>

    <h4>
        Status:&nbsp;
        <span style={{ color: status.loggedIn ? "green" : "red" }}>
            { status.loggedIn ? "Logged in" : "Not Logged In" }
        </span>
    </h4>

    { status.loggedIn && <button onClick={ status.logout }>Logout</button> }
    { !status.loggedIn && <button onClick={ status.login }>Login</button> }

    { status.loggedIn &&
        <h3>Hi { user.userDetails.name }</h3>
    }
</main>

Logged out state: NextJS logged out state

Logged in state: NextJS logged in state

Keep in mind, our Context API global "object" or data is accessible by every page/component. So in this case, if we were to navigate to /account for example, the state would be retained and accessible in the same, global way.

This was a very very basic example of how to set up a Context API provider, and inject it into our NextJS app, then use the accessible functions and data based on the state, I hope this helps anyone, if it did, leave a little reaction on this post, greatly appreciated!

I've also recently started a YouTube channel. I'd love to see you over there and I welcome new subscribers. I'm posting coding tutorials, mostly on JavaScript, React and NextJS! 👇👇👇👇

Alan Montgomery - Coding Tutorials

Thanks for reading! Leave a comment below if you liked this!

Isaac Way's photo

This is well written but it feels like it's half of a tutorial. The above implementation is completely unusable because the value of the context is going to be reset any time the user. I'm sorry but I'm just so frustrated. Every. Single. Tutorial about Contexts and NextJS is like this. I keep searching and searching for an example of actually using these things but all I get is articles like this one, where you're just using the context as a place to store data for a single page.

Contexts are useful because they allow components and pages to share state, right? Well then, what is the point of using one if your state is just going to be reset whenever you redirect? What is the point of having login data that disappears when you change pages? Am I missing something here? I do not understand how the above example is useful in any way if the context is just reset to the default value every time you redirect to a new page.

Edit: Yeah I was missing something. Sorry lol. I just needed to use <Link> to redirect instead of vanilla <a> tag. Maybe include that in the article for future dummies?

Alan Montgomery's photo

Yeah definitely could include that for people who are confused about modern-day SPA that don't actually "redirect" or "reset". I guess I just assumed that readers would understand the architecture and structure of Next (and React in that case) in terms of links and how you should not refresh your app, because of course, you will lose your state. The state is always available in any case until the page is refreshed.

Hopefully, this tutorial was then slightly helpful after all, and sorry if you found it unusable, however, I did try to cover what was needed.

Pablo Bernal's photo

I'm still confused about how the state is stored across navigations. I'd think either you don't perform a full navigation or you store state in local/session storage. Which is it when using Context API on the app component?

Is the Context API somehow using local storage under the hood, abstracted from us, or does this only work when using NextJS as CSR and not when using SSG?

Edit: I understand now! The key is that even for SSG Nextjs actually performs a client-side navigation when using the Link component, instead of a full page refresh, as they mention in the docs for routing:

nextjs.org/docs/routing/introduction

"The Next.js router allows you to do client-side route transitions between pages, similar to a single-page application.

A React component called Link is provided to do this client-side route transition."

Dinys Monvoisin's photo

The issue I found using React Context API is that the number of nested components increase as your props increase.

People should determine at first hand how complex their project would be to decide using React Context API or a state management library like Redux.

Alan Montgomery's photo

Yeah a definite valid point.