Light/Dark Mode toggle in React JS using Context API

ยท

6 min read

Hello Dev, Now a days most of the websites have dark and light mode. In this post we will learn how to do that in your React application using the Context API.

Packages:

  1. react-switch To implement the toggle switch (you can use button also)

  2. react-icons To use sunny and moon icon (you can use any icon packages)

A quick demo

To access the repo here

Concept to understand here:

  • Context API - Provider and Consumer.

  • The Provider act as a Global state. So the Root component of the project should be wrapped by the Provider

  • Access the values stored in the provider using the Consumer at anywhere in your component

  • The provider always holds the value(state) and methods that modify the state

First create a Provider.js

Provider.js

import React, { useState, createContext } from "react";

export const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
  const [mode, setTheme] = useState("light");
  return (
    <ThemeContext.Provider
      value={{
        mode,
        setTheme: () => setTheme(mode === "dark" ? "light" : "dark")
      }}
    >
      {children}
    </ThemeContext.Provider>
  );
};

export default ThemeProvider;

createContext() creates a context object. Then defining a state named mode by default it as light value. And the setTheme is an action that modifies the mode value. Then creating the Provider component by default it accepts value as a prop. Notice that: The value prop has mode and setTheme properties.

The provider component returns a children. i.e where ever we wrap this provider that component will be considered as root of this context provider.

From the above code we initialized ThemeContext and Created our ThemeProvider.

Create theme.js

theme.js

export const theme = {
  light: {
    color: "#03dac5",
    backgroundColor: "#ffffff",
    highlight: "#fdb813"
  },
  dark: {
    color: "#bb86fc",
    backgroundColor: "#121212",
    highlight: "#ffffff"
  }
};

Theme is just an object containing values corresponding to the modes.

Wrap the ThemeProvider in the root component

index.js

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";

import ThemeProvider from "./Provider";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <ThemeProvider>
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </ThemeProvider>,
  rootElement
);

Here, the root component is index.js. Now the Provider is available globally, we can use the consumer in any where inside our components to access the value from the provider.

Access the values from the provider

App.js

import React, { useContext } from "react";

import { theme } from "./theme";

import { ThemeContext } from "./Provider";

const getStyles = (mode) => ({
  header: {
    fontSize: 34,
    fontWeight: "400"
  },
  app: {
    height: "100%",
    width: "100%",
    padding: 16,
    backgroundColor: theme[mode].backgroundColor
  },
  text: {
    fontWeight: "200",
    color: theme[mode].color
  },
  theme: {
    color: theme[mode].highlight
  }
});

export default function App() {
  const { mode } = useContext(ThemeContext);
  const styles = getStyles(mode);
  return (
    <div style={styles.app}>
      <h1 style={(styles.header, styles.text)}>
        Have a nice day... DEV!
      </h1>
      <h2 style={styles.text}>
        Current theme is <span style={styles.theme}>{mode}</span> mode
      </h2>
    </div>
  );
}

From the above code, we are trying to access the mode value. So at first we need to say what context object we are trying to access(ThemeContext).

You notice that, mode value is passed to styles, here based on the mode we are changing the text color and background color.

Time to create a toggle switch

ThemeSwitch.js

import React, { useContext } from "react";

import Switch from "react-switch";
import { IoMdSunny, IoMdMoon } from "react-icons/all";

import { ThemeContext } from "./Provider";

const getStyles = (mode) => ({
  switch: {
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    height: "100%",
    fontSize: 35,
    paddingLeft: mode === "dark" ? 30 : 10
  }
});

const ThemeSwitch = () => {
  const { setTheme, mode } = useContext(ThemeContext);
  const styles = getStyles(mode);
  return (
    <Switch
      checked={mode === "light" ? true : false}
      height={50}
      width={120}
      offColor="#1d1f2f"
      onColor="#FDB813"
      checkedIcon={
        <IoMdSunny style={styles.switch} color="white" className="light" />
      }
      uncheckedIcon={
        <IoMdMoon style={styles.switch} color="white" className="dark" />
      }
      onChange={setTheme}
    />
  );
};

export default ThemeSwitch;

Here, we are handling the setTheme method when the toggle switch is clicked. And based on the mode we are updating the icons and colors.

Finally add the toggle switch inside the component

App.js

import React, { useContext } from "react";

import { theme } from "./theme";

import { ThemeContext } from "./Provider";
import ThemeSwitch from "./ThemeSwitch";

const getStyles = (mode) => ({
  header: {
    fontSize: 34,
    fontWeight: "400"
  },
  app: {
    height: "100%",
    width: "100%",
    padding: 16,
    backgroundColor: theme[mode].backgroundColor
  },
  text: {
    fontWeight: "200",
    color: theme[mode].color
  },
  theme: {
    color: theme[mode].highlight
  }
});

export default function App() {
  const { mode } = useContext(ThemeContext);
  const styles = getStyles(mode);
  return (
    <div style={styles.app}>
      <h1 style={(styles.header, styles.text)}>Have a nice day... DEV!</h1>
      <h2 style={styles.text}>
        Current theme is <span style={styles.theme}>{mode}</span> mode
      </h2>
      <ThemeSwitch />
    </div>
  );
}

Added <ThemeSwitch /> in App.js. Now play with toggle switch to notice the changes.

That's all, you can add this ThemeSwitch component any where inside your project to change the theme.

๐ŸŽ‰ Tada... We are done...

Thanks ๐Ÿ˜ƒ for the read...

Drop a โ™ฅ๏ธ if this content is helpful...

Suggestions and doubts are always welcome in the comment section...