ReactHustle

How to Use React Context in NextJS with Typescript

Jasser Mark Arioste

Jasser Mark Arioste

How to Use React Context in NextJS with Typescript

Hello, hustlers! In this tutorial, you'll learn how to create and define a React Context in NextJS with Typescript.

Introduction #

There are many ways to manage the state in react and react context is one the most common ways to do it. Context gives us the ability to pull the data without having to pass it down to the component tree via props.

This is all good. However, one of the questions I always ask is, how do you use it with typescript?

Tutorial Objectives #

We'll create a ThemeProvider component and a useTheme hook that uses ReactContext under the hood. And after this tutorial, you should be able to learn the following:

  1. Create a custom hook that uses react context as a best practice.
  2. Define the types for the state and props.
  3. Create a <Provider/> component that wraps that <Context.Provider/>

Step 1 - Defining the Custom Hook #

One of the best practices in programming is when you code, you always start with: how do you want to use the API?

In this case, I want a theme state and a setTheme function returned by our useTheme hook. In other words, we want to start with what our custom hook returns or provides.

So when we use it, it should be like this:

import React from "react";
import useTheme from "./context/useTheme";

const Component = () => {
  const { theme, setTheme } = useTheme();
  return <div></div>;
};

export default Component;
123456789

All right, so let's define our hook. First, let's define the state returned by our hook and the hook itself.

import React, { useState } from "react";
// define the props
type Themes = "dark" | "light"  | "system";
type ThemeState = {
  theme: Themes;
  setTheme(theme: Themes): void;
};

const useTheme = (): ThemeState => {
  // use useState for now. we'll change this later.
  const [theme, setTheme] = useState<Themes>("dark");
  return { theme, setTheme };
};

export default useTheme;
123456789101112131415

In the above code, we defined a custom hook that gives us the API we want (theme and setTheme). However, it's currently using theuseState hook instead of useContext. We'll change this in the next step.

Step 2 - Creating the Context #

Next, let's use the useContext hook. We only have to add or change a couple of lines:

import { createContext, useContext } from "react";
// define the props
type Themes = "dark" | "light" | "system";
type ThemeState = {
  theme: Themes;
  setTheme(theme: Themes): void;
};

// 1. create a context with ThemeState and initialize it to null
const ThemeContext = createContext<ThemeState | null>(null);

const useTheme = (): ThemeState => {
  // 2. use the useContext hook
  const context = useContext(ThemeContext);

  // 3. Make sure it's not null!
  if (!context) {
    throw new Error("Please use ThemeProvider in parent component");
  }

  return context;
};

export default useTheme;
123456789101112131415161718192021222324

Now, if we use this in our pages/index.tsx page right now, it will currently throw an error since we haven't created the ThemeProvider yet.

// pages/index.tsx
import useTheme from "@/components/context/useTheme";

export default function Home() {
  const { theme } = useTheme();
  return <div>{theme}</div>;
}
1234567
React use context Error.

Step 3 - Creating the ThemeProvider #

To fix the error above, we'll create the ThemeProvider component that uses the ThemeContext.Provider component under the hood.

// ... omitted for brevity
// 1. create a context with ThemeState and initialize it to null
const ThemeContext = createContext<ThemeState | null>(null);

// omitted for brevity...
export const ThemeProvider = (props: PropsWithChildren) => {
  const [theme, setTheme] = useState<Themes>("system");
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {props.children}
    </ThemeContext.Provider>
  );
};
12345678910111213

Explanation:

In line 3, we use useState to store the state for the theme. In line 5, we use the <ThemeContext.Provider> to provider whatever state we had from the useState hook.

The value prop will have the type of ThemeState, since that's what we declared earlier. You can verify this by using the intellisense feature of your editor (mine is VS code)U

React.useContext intellisense feature&nbsp; vs code

Now our full code looks like this:

import { PropsWithChildren, createContext, useContext, useState } from "react";
// define the props
type Themes = "dark" | "light" | "system";
type ThemeState = {
  theme: Themes;
  setTheme(theme: Themes): void;
};

// 1. create a context with ThemeState and initialize it to null
const ThemeContext = createContext<ThemeState | null>(null);

const useTheme = (): ThemeState => {
  // 2. use the useContext hook
  const context = useContext(ThemeContext);

  // 3. Make sure it's not null!
  if (!context) {
    throw new Error("Please use ThemeProvider in parent component");
  }

  return context;
};

export const ThemeProvider = (props: PropsWithChildren) => {
  const [theme, setTheme] = useState<Themes>("system");
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {props.children}
    </ThemeContext.Provider>
  );
};

export default useTheme;
123456789101112131415161718192021222324252627282930313233

Step 4 - Using the ThemeProvider in _app.tsx #

So that all pages and their children can use the useTheme hook. We'll have to use the ThemeProvider in our _app.tsx file.

// pages/app.tsx
import { ThemeProvider } from "@/components/context/useTheme";
import type { AppProps } from "next/app";

export default function App({ Component, pageProps }: AppProps) {
  return (
    <ThemeProvider>
      <Component {...pageProps} />
    </ThemeProvider>
  );
}
1234567891011

Step 5 (Bonus) - Setting initial state via Props #

If we want to set the initial state via props, it's also possible since ThemeProvider is a react component and we can add props to it depending on our requirements.

// define the props
type ThemeProviderProps = {
  initialTheme: Themes;
} & PropsWithChildren;

export const ThemeProvider = (props: ThemeProviderProps) => {
 //set the initial state using props
  const [theme, setTheme] = useState<Themes>(props.initialTheme);
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {props.children}
    </ThemeContext.Provider>
  );
};
1234567891011121314

Conclusion #

You learned how to create and use the React Context API in NextJS app with Typescript. You should use React Context when you need a state in multiple pages or parts of your application.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on GitHub.

Resources #

Credits: Image by David Mark from Pixabay

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle