How to do Dark Mode with React Hooks
Joseph Chow
Posted on December 29, 2020
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;
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);
}
...
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);
}
...
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"));
}, []);
};
...
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);
}
};
...
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>
);
...
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>
);
}
...
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/
Posted on December 29, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.