How to Use React Context in NextJS with Typescript
Jasser Mark Arioste
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:
- Create a custom hook that uses react context as a best practice.
- Define the types for the
state
andprops
. - 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
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
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