TypeOfNaN

Toggling Light/Dark Theme in React with useContext

Nick Scialli
September 14, 2019

New — Check out my free newsletter on how the web works!

Introduction

This post shows one method to use React’s useContext and useState hooks to implement dark/light mode toggling. The relevant files are src/ThemeProvider.tsx, src/index.tsx, and src/App.tsx.

This project uses Typescript, but the same functionality can be achieved in javascript by removing the types.

Demo and Full Code

You can see a working version of this simple app on Netlify here:

Demo site

The full code can be found on github here:

Full code

Diving into the Code

ThemeProvider.tsx

In our ThemeProvider component, we define our Theme as being either light or dark and we define our ThemeContext as being an object with two properties: theme and toggleTheme (the theme and ability to toggle the theme will be made available to other components via the useContext hook).

We have to make sure to export the ThemeContext object we create using React.createContext.

Within the ThemeProvider component, we maintain our theme state using the useState hook and create a toggleTheme function that will toggle the state between light and dark.

For simplicity, we simple set the document body’s color and backgroundColor styles based on whether the theme state is currently light or dark. Finally, we export our ThemeContext Provider with value set to and object with theme and toggleTheme properties. We then render children within our ThemeContext.Provider component.

import React, { useState } from "react";
typescript;
type Theme = "light" | "dark";
type ThemeContext = { theme: Theme; toggleTheme: () => void };

export const ThemeContext = React.createContext<ThemeContext>(
  {} as ThemeContext
);

export const ThemeProvider: React.FC = ({ children }) => {
  const [theme, setTheme] = useState<Theme>("light");
  const toggleTheme = () => {
    setTheme(theme === "light" ? "dark" : "light");
  };

  const color = theme === "light" ? "#333" : "#FFF";
  const backgroundColor = theme === "light" ? "#FFF" : "#333";

  document.body.style.color = color;
  document.body.style.backgroundColor = backgroundColor;

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

index.tsx

In our index file, we simply wrap the entire app in our new ThemeProvider component. Of course we don’t need to do this at the app level in real projects, we just need to make sure that any components that need theme or toggleTheme are within the child tree of our provider.

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ThemeProvider } from './ThemeProvider';

ReactDOM.render(
  <ThemeProvider>
    <App />
  </ThemeProvider>,
  document.getElementById('root')
);

App.tsx

In the App component, we use the useContext hook to gain access to our theme string and toggleTheme function. We create a simple button that can toggle the theme and only use theme to determine what we show the user: “Switch to dark mode” or “Switch to light mode”

import React, { useContext } from 'react';
import { ThemeContext } from './ThemeProvider';

const App: React.FC = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <div>
      <div>Hi friend!</div>
      <button onClick={toggleTheme}>
        Switch to {theme === 'light' ? 'dark' : 'light'} mode
      </button>
    </div>
  );
};

export default App;

And that’s it!

🎓 Learn how the web works

Over my 20 year career in tech, I have noticed that an often-neglected part of interview prep is learning to explain technical concepts clearly. In my free newsletter, How the Web Works, I provide simple, bite-sized explanations for various web topics that you can use in your interview prep (or just to gain a better understanding of the web).

Signing up is free, I never spam, and you can unsubscribe any time.

Sign up for the newsletter »
Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli