Nowadays most web/mobile applications have a dark mode presentation. That's why today I will explain to you how to do it in reactJs applications. There are several ways to achieve it, I will do it using CSS and HTML.
In Addition, I'm going to add a listener to detect if the dark/light mode of your browser has changed, so the app will change styles automatically.
1. Create a react app
Choose a location in your terminal and run this:
npx create-react-app my-dark-mode-app
2. Add CSS
Let's modify some CSS. First, we are going to create some global css variables to use. Go to index.css
and add this lines at the beginning of the file:
:root {
--background-color: #fefefe;
--text-color: #3f3f3f;
}
.dark-mode {
--background-color: #3f3f3f;
--text-color: #ffffff
}
In the same, file add these properties to body
:
background-color: var(--background-color);
color: var(--text-color);
Those previous styles will help us to change dark mode styles: background color and text colors.
Now go to the file App.css
and make sure you just have these styles:
.App {
text-align: center;
}
.App-header {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
}
3. Modify JavaScript
We are going to work with a simple view, so go to App.js
and edit the return
:
return (
<div className="App">
<header className="App-header">
<p>
This is an example of dark/light mode using reactJs !!
</p>
</header>
</div>
);
4. Change Dark Mode
Inside App.js
we should create a useState
hook to save the mode of our application:
const [mode, setMode] = useState('light')
Don't forget to import useState
import {useState} from "react";
Also, with this new method we're going to save in our state the current mode, then add or remove the dark-mode
classname of the body
tag.
const onSelectMode = (mode) => {
setMode(mode)
if (mode === 'dark')
document.body.classList.add('dark-mode')
else
document.body.classList.remove('dark-mode')
}
Then, in this same file let's add some icons after the p
tag:
<div>
{
mode === 'dark' ?
<svg className={'icons'} xmlns="http://www.w3.org/2000/svg" width={33} viewBox="0 0 24 24" fill="#f1e408"
onClick={() => onSelectMode('light')}>
<rect fill="none"/>
<path
d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/>
</svg> :
<svg className={'icons'} xmlns="http://www.w3.org/2000/svg" width={33} viewBox="0 0 24 24" fill="#000000"
onClick={() => onSelectMode('dark')}>
<rect fill="none" height="24" width="24"/>
<path
d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/>
</svg>
}
</div>
As you can see, we are calling the onSelectMode
method with the mode as parameter. I'm showing a sun
icon if we are in dark mode, and a moon
icon if light mode is active. Clicking in those icons we can change our mode.
Now let's add a missing style in App.css
:
.icons {
cursor: pointer;
}
So far our App.js
looks like this:
import './App.css';
import {useState} from "react";
function App() {
const [mode, setMode] = useState('light');
const onSelectMode = (mode) => {
setMode(mode)
if (mode === 'dark')
document.body.classList.add('dark-mode')
else
document.body.classList.remove('dark-mode')
}
return (
<div className="App">
<header className="App-header">
<p>
This is an example of dark/light mode using reactJs !!
</p>
<div>
{
mode === 'dark' ?
<svg className={'icons'} xmlns="http://www.w3.org/2000/svg" width={33} viewBox="0 0 24 24" fill="#f1e408"
onClick={() => onSelectMode('light')}>
<rect fill="none"/>
<path
d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"/>
</svg> :
<svg className={'icons'} xmlns="http://www.w3.org/2000/svg" width={33} viewBox="0 0 24 24" fill="#000000"
onClick={() => onSelectMode('dark')}>
<rect fill="none" height="24" width="24"/>
<path
d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"/>
</svg>
}
</div>
</header>
</div>
);
}
export default App;
Until this point, we are able to change manually our dark/light mode. What if we want that our app automatically detects if dark/light mode of our devices has changed?.
Let's do it!
5. Adding a Listener
First, we need to know how we can detect if dark/light mode has changed in our browser(don't copy yet):
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => onSelectMode(e.matches ? 'dark' : 'light'));
The e.matches
will return true if dark mode has been enabled.
Let's create a useEffect
hook, to create our listener the first time our app loads.
useEffect(() => {
// Add listener to update styles
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => onSelectMode(e.matches ? 'dark' : 'light'));
// Setup dark/light mode for the first time
onSelectMode(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
// Remove listener
return () => {
window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', () => {
});
}
}, []);
We must know that the Listener
is going to detect when dark mode changes. However, the first time our app loads we need to know the current mode of our browser, that's why I added this line in our useEffect
:
onSelectMode(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
Conclusion
We created a reactJs application, in which we were able to implement dark mode using CSS to change the background color and text color.
Every text inside the body
tag will be changed in dark/light mode. Then we recognize the browser mode to show our page in dark or light mode automatically.
You can see the demo: dark-mode-reactjs-demo
Github repo:
Top comments (4)
Thanks for the post. It's helpful.
I'd like to suggest to rename the "mode" param that is defined in "onSelectMode" function. The param has the same name as state. It's a little bit confusing.
Have a nice day!
Hey! yeah I didn’t see that, thanks !
Thanks!
So matchMefia function detects the color theme based on WHAT? Color, Class, MediaQuery or System?