DEV Community

Cover image for Dark Mode in React using Hooks
Rohit kumar singh
Rohit kumar singh

Posted on

52 7

Dark Mode in React using Hooks

Why need Dark Mode ?

Because it looks cool ๐Ÿ˜Ž
Correct but its not enough. Dark mode is known to save a lot of energy on AMOLED screens. Android case studies focused on popular Google apps like YouTube have shown that the power savings can be up to 60%.

Let's see how to implement it in React by using hooks and browser's localStorage.
We will use here facebook's react-boilerplate.
Clone it first by using the command npx create-react-app dark-mode, after cloning, change the root directory to dark-mode by using cd dark-mode and to run the application npm start, use this create-react-app for more details.

Let's add some darkness ๐Ÿ˜ƒ

Create CSS Files

// light-theme.css

html[data-theme="light"]  {
  --color: rgb(5, 5, 5);
  --background-color: rgb(250, 250, 250);
}
Enter fullscreen mode Exit fullscreen mode
// dark-theme.css

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

As of now, I have added only two color variables, later you can add as many color variables for your project.
Don't hardcode color in any css files or in any inline styling, use only defined color variables.

// 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

I have used those color variables in App.css file.

Create DarkModeToggle component

click to see the folder structure
Create DarkMode folder, add index.js and index.css files.

// DarkMode/index.js

const DarkModeToggle = () => {
  const [isDark, setIsDark] = useState(localStorage.getItem("theme") === "dark" ? true : false);
  useEffect(() => {
    document
    .getElementsByTagName("HTML")[0]
    .setAttribute("data-theme", localStorage.getItem("theme"));
  },[]);
Enter fullscreen mode Exit fullscreen mode

Using useState hook to store the current user theme preference, get the current user preference from localStorage.
Suppose you are running the application for first time, you won't get the user theme preference in browser's localStorage, in that case false get set to the isDark hook and applied light theme to the application.
I have used browser's localStorage to set the choosen user theme preference and update it while theme toggling.
Set HTML data-theme attribute accordingly with current user theme preference.

Note: The data-* attribute is used to store custom data private to the page or application. The data-* attribute gives us the ability to embed custom data attributes on all HTML elements.

// handles user theme preference change

const toggleThemeChange = () => {
    if (isDark === false) {
      localStorage.setItem("theme", "dark");
      document
        .getElementsByTagName("HTML")[0]
        .setAttribute("data-theme", localStorage.getItem("theme"));
        setIsDark(true);
    } else {
      localStorage.setItem("theme", "light");
      document
        .getElementsByTagName("HTML")[0]
        .setAttribute("data-theme", localStorage.getItem("theme"));
        setIsDark(false);
    }
  }
Enter fullscreen mode Exit fullscreen mode

This method will get triggered when we toggle the theme from light to dark or vice-versa. It will update the state isDark based on current theme choosen and simultaneously update the data-theme attribute. data-theme attribute helps application to determine which color schemes need to applied either dark html[data-theme="dark"] or light html[data-theme="light"].

// templete for theme toggle button

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

returning the html toggle element for switching the theme.

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          <DarkModeToggle />
        </a>
      </header>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Add this DarkModeToggle component wherever you want to place it.

Let's checkout the application after the Darkness applied

GitHub repo link in case if you want to directly fork it for your project.

Sentry blog image

How I fixed 20 seconds of lag for every user in just 20 minutes.

Our AI agent was running 10-20 seconds slower than it should, impacting both our own developers and our early adopters. See how I used Sentry Profiling to fix it in record time.

Read more

Top comments (11)

Collapse
 
joeattardi profile image
Joe Attardi โ€ข

Nice! There is also a media query, prefers-color-scheme, that you can use to automatically detect if the user's operating system is set to dark mode!

developer.mozilla.org/en-US/docs/W...

Collapse
 
rohit1508 profile image
Rohit kumar singh โ€ข

Correct Joe, I will use that in my next article. But prefers-color-scheme not supported by many of the browsers as of now. so, I thought of going with this approach.

Collapse
 
fomenkogregory profile image
Greg โ€ข

Nice post! But just a little tip, this expression

  const [isDark, setIsDark] = useState(localStorage.getItem("theme") === "dark" ? true : false)

can be written in a bit cleaner way, if you get rid of ternary operator:

  const [isDark, setIsDark] = useState(localStorage.getItem("theme") === "dark")
Collapse
 
rohit1508 profile image
Rohit kumar singh โ€ข

Thanks Greg, we can avoid the ternary.
But suppose a case if we didn't get theme from browser's localStorage, then it will throw undefined and assigned to isDark, but yes we can handle that later. To make the article more understandable I made it like that, so I will convey the code more clearly.

Collapse
 
aravindballa profile image
Aravind Balla โ€ข โ€ข Edited

Great write up! I did the same using class names on <body> for my website recently. CSS variables are really powerful.

Collapse
 
rohit1508 profile image
Rohit kumar singh โ€ข

Thanks Aravind ๐Ÿ˜Š

Collapse
 
gillarohith profile image
Rohith Gilla โ€ข

Amazing

Collapse
 
rohit1508 profile image
Rohit kumar singh โ€ข

Thanks Rohith

Collapse
 
guriksolves profile image
Guri-ksolves โ€ข

Nice one bro๐Ÿ‘๐Ÿ‘

Collapse
 
rohit1508 profile image
Rohit kumar singh โ€ข

thanks ๐Ÿ˜€

Collapse
 
waleedd322 profile image
walid โ€ข โ€ข Edited

why we dont need context api or redux here ? should we implement that ? some other examples use context api, but here it works without it, even if we have ten pages, and one button ?

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

๐Ÿ‘‹ Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someoneโ€™s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay