DEV Community

Cover image for How to do Dark Mode with React Hooks
Joseph Chow
Joseph Chow

Posted on • Updated on

How to do Dark Mode with React Hooks

Dark Mode is one of the latest features being widely adopted across apps and websites. Dark colours can prolong the battery life of mobile devices. The savings, according to Google with the Dark Mode offered on the YouTube app, allows a 15 percent energy reduction at 50 percent brightness and a huge 60 percent reduction at 100 percent bightness. Apple confirmed moving forward in 2020, their phones will feature OLED screens.

Check out the completed demo here. The completed code for this demo can be found here.

Why React Hooks

We know with React components allows top-down data flow that helps us organize an application into small manageable pieces of UI. However, with only the component model, components become large and difficult to refactor. In addition, logic may need to be duplicated between components. Hooks are great because they allow us to organize logic within a component into discreet units that are easy to reuse.

However, we often can’t break complex components down any further because the logic is stateful and can’t be extracted to a function or another component. - Dan Abramov

Implementing Hooks

For simplicity in demonstrating how to implement React Hooks with Dark Mode, we will be using localStorage so the browser will remember our user's preference upon reload. In more complex web applications, the user's preference can be stored in a database. So let's begin with Facebook's standard React boilerplate. Clone the repo, change directory into the newly created project folder, and run the application. For more information, check out the docs.

npx create-react-app dark-mode;
cd dark-mode;
npm start;
Enter fullscreen mode Exit fullscreen mode

Next, let's add our CSS colour variables. We can start with just a colour and a background colour, but you can add more if you would like to build out a larger colour scheme. This way, we are modularizing our colour styling in a consistent way that can be easily implemented across our site.

// App.css

...

html[data-theme="light"]  {
  --color: rgb(5, 5, 5);
  --background-color: rgb(250, 250, 250);
}

html[data-theme="dark"]  {
  --color: rgb(250, 250, 250);
  --background-color: rgb(5, 5, 5);
}

...

Enter fullscreen mode Exit fullscreen mode

To set the variables, replace the header CSS in the App.css file like so:

// App.css

...

.App-header {
  background-color:var(--background-color);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  font-size: calc(10px + 2vmin);
  color:var(--color);
}

...

Enter fullscreen mode Exit fullscreen mode

Creating theme-toggle component

Create a new file in the src* folder and name it **ThemeToggle, or something to that effect. This is currently a starter project, if you like, you can create a separate ThemeToggle directory. Here, we are going to 'use' the useState hook to store the current theme preference.

// ThemeToggle.js

...

  const ThemeToggle = () => {
    const [darkMode, setDarkMode] = useState(
      localStorage.getItem("theme") === "dark" ? true : false
    );
    useEffect(() => {
      document
        .getElementsByTagName("HTML")[0]
        .setAttribute("data-theme", localStorage.getItem("theme"));
    }, []);
  };

...

Enter fullscreen mode Exit fullscreen mode

If a user is accessing the site for the first time, there will not be a preference set in the localStorage. Our Strict Equality Comparison operator will throw false and the darkMode hook to apply the light theme across the application.

This is done using the HTML data- attribute, which allows us to store information in semantic HTML elements. We will use it in the method that is going to be triggered to switch the theme to dark, or vice-versa. Using the same logic, we can also call our setDarkMode hook from earlier to set the theme in localStorage.

// ThemeToggle.js

...

  const switchThemes = () => {
    if (isDark === false) {
      localStorage.setItem("theme", "dark");
      document
        .getElementsByTagName("HTML")[0]
        .setAttribute("data-theme", localStorage.getItem("theme"));
      setDarkMode(true);
    } else {
      localStorage.setItem("theme", "light");
      document
        .getElementsByTagName("HTML")[0]
        .setAttribute("data-theme", localStorage.getItem("theme"));
      setDarkMode(false);
    }
  };

...

Enter fullscreen mode Exit fullscreen mode

The last thing we need to do in ThemeToggle is return the button to be imported.

// ThemeToggle.js

...

  return (
    <label className="switch">
      <input
        type="checkbox"
        defaultChecked={isDark}
        onChange={() => toggleThemeChange()}
      />
      <span className="slider round" />
    </label>
  );

...

Enter fullscreen mode Exit fullscreen mode

Now, to test, we can import it into our App.js.

// App.js

...

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          React Hooks Dark Mode Starter
        </p>
        <ThemeToggle />
      </header>
    </div>
  );
}

...

Enter fullscreen mode Exit fullscreen mode

Congratulations!!

Now, you have a usable element that can place wherever you want in your application. If you want to take a look at the complete code, or create your own fork, check out my repo here.

Originally published at https://www.josephc.how/react-hooks-dark-mode/

Discussion (0)