A Complete Guide to Implementing Dark Mode in React (2024)

Want to make your website more accessible and trendy by providing dark mode option? Here is the step-by-step guide on how to implement dark mode in React with the best practices.

A Complete Guide to Implementing Dark Mode in React (3)

Step 1: Create React app using Vite (skip if already created)

Let’s start by creating the React App. I will set up the project with one of the most popular build tools called Vite. One of the great advantages is that it simplifies and speeds up the development process.

Use the following command to set up the project.

$ yarn create vite

Vite will ask to write a Project name, Select a framework and Select a variant.

For this guide, I’ll choose react as a framework and react-ts as a variant.

A Complete Guide to Implementing Dark Mode in React (4)

Once you have your app set up, navigate to your project, install the dependences and we are ready to go.

Step 2: Create theme context

Open a theme-context.ts file in the /src/contexts folder and create the theme context.

// theme-context.tsimport { createContext } from 'react';export const ThemeContext = createContext({
theme: '',
setTheme: (theme: string) => {},
});

The context will receive “light” or “dark” as an input and will return the same as an output.

Step 3: Create theme-variables.scss file with variables for the light and dark themes

Open a folder in the src and name it styles. Within the folder create a theme-variables.scss file.

A Complete Guide to Implementing Dark Mode in React (5)

The theme-variables.scss file will contain color alternatives for the light and dark modes.

The file will consist of 2 objects named light and dark, where each theme will have the same key with different color options.

Having the same key in both objects is a must.

// theme-variables.scss$themes: (
light: (
colorHeaderFooter: #fff,
colorText: #000,
colorBackground: #f8f8fa,
),
dark: (
colorHeaderFooter: #36394c,
colorText: #eff2f7,
colorBackground: #222736,
),
);

Next, we are going to create a mixin called themify to generate the dark and light themes.

Then we are going to create a function called themed that will receive a color key as an argument and will return the color value for the current theme.

// theme-variables.scss@mixin themify($themes) {
@each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
@each $key, $submap in $map {
$value: map-get(map-get($themes, $theme), '#{$key}');
$theme-map: map-merge(
$theme-map,
(
$key: $value,
)
) !global;
}
@content;
$theme-map: null !global;
}
}
}
@function themed($key) {
@return map-get($theme-map, $key);
}

Once the file is ready, navigate to the src/App.tsx file.

Step 4: Wrap the App component in the theme context

In the App.tsx file import Theme context created in the Step 2.

// App.tsximport { ThemeContext } from './contexts/theme-context';

Create a state for the current theme and for now let’s set the default value to “light”.

In the last step we will cover how to detect the browser’s mode and decide on the default mode based on that.

// App.tsxconst [theme, setTheme] = useState('light');

Last but not least, wrap the entire component within the Theme provider.

// App.tsx<ThemeContext.Provider value={{ theme, setTheme }}>
<div className={`theme-${theme}`}>
<Layout>
// Your code here
</Layout>
</div>
</ThemeContext.Provider>

So with the help of the context provider we will have the current theme name and will use it in the className of the theme wrapper.

After the compilation we will have className of the div theme-light or theme-dark.

Now we are ready to use the color variables in any of the .scss files of the project.

Import the theme-variables.scss wherever necessary.

Use @include directive to get the color value with the help of themed function declared above. Below we are setting the background of the header component.

// header.tsx@import '../styles/theme-variables.scss';.header {
@include themify($themes) {
background: themed('headerFooterBackground');
}
}

After compilation we will have the following:

.theme-light .header {
background: #fff;
}
.theme-dark .header {
background: #36394c;
}

Step 5: Create a layout component with a light/dark mode toggle button

Now let’s create a simple toggle button to switch between themes.

// header.tsximport { FC, useContext } from 'react';
import { ThemeContext } from '../contexts/theme-context';
import logoIcon from '../images/logo-icon.png';
import './styles.scss';
const Header: FC = () => { const { theme, setTheme } = useContext(ThemeContext); const handleThemeChange = () => {
const isCurrentDark = theme === 'dark';
setTheme(isCurrentDark ? 'light' : 'dark');
};
return (
<header className="header">
<div className="header-content">
<a href="/" className="logo-section">
<img src={logoIcon} alt="logo" />
<span>Light/Dark mode app</span>
</a>
<div className="toggle-btn-section">
<div className={`toggle-checkbox m-vertical-auto`}>
<input
className="toggle-btn__input"
type="checkbox"
name="checkbox"
onChange={handleThemeChange}
checked={theme === 'light'}
/>
<button type="button" className={`toggle-btn__input-label`} onClick={handleThemeChange}></button>
</div>
</div>
</div>
</header>
);
};
export default Header;

On toggle click the theme value will be changed with the help of handleThemeChange function declared above.

Step 6: Detect the browser’s default mode if any to set up the default theme

In the App.tsx file we set the theme’s default value as light.

So, whenever user enters the website for the first time the theme value will be set to light.

In this step I will show how to detect the browser’s default theme to use it as a default. We can easily check it with the help of matchMedia.

// App.tsxconst isBrowserDefaultDark = () => window.matchMedia('(prefers-color-scheme: dark)').matches;

The isBrowserDefaultDark fuction will return true if the browser’s mode is dark.

Now we can set the default for the browser in the App.tsx.

// App.tsxconst [theme, setTheme] = useState(isBrowserDefaultDark() ? 'dark' : 'light');

Step 7: Set the default theme in the localStorage

Last but not least, we are going to save the theme in the localStorage and everytime user enters your website the default theme will be taken from the localStorage.

We are going to the theme in the localStorage in the header.tsx where our toggle button is placed.

In the function handleThemeChange where we were changing the theme on toggle click let’s add the following line:

// header.tsxconst handleThemeChange = () => {
const isCurrentDark = theme === 'dark';
setTheme(isCurrentDark ? 'light' : 'dark');
localStorage.setItem('theme', isCurrentDark ? 'light' : 'dark');
};

Now, when we have the default theme stored in the localStorage we can use it in the App.tsx file in setting the theme.

Let’s create a function that will return the default theme as soon as user enters the website. The function will the check if user has the theme stored in the localStorage and return the value if any, otherwise it will check the browser’s mode and return the default.

// App.tsx// Detecting the default theme
const isBrowserDefaultDark = () => window.matchMedia('(prefers-color-scheme: dark)').matches;
const getDefaultTheme = (): string => {
const localStorageTheme = localStorage.getItem('default-theme');
const browserDefault = isBrowserDefaultDark() ? 'dark' : 'light';
return localStorageTheme || browserDefault;
};
const [theme, setTheme] = useState(getDefaultTheme());

Our dark mode app is ready now.

Thank you for reading the article.

A Complete Guide to Implementing Dark Mode in React (2024)
Top Articles
Latest Posts
Article information

Author: Duane Harber

Last Updated:

Views: 6069

Rating: 4 / 5 (51 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Duane Harber

Birthday: 1999-10-17

Address: Apt. 404 9899 Magnolia Roads, Port Royceville, ID 78186

Phone: +186911129794335

Job: Human Hospitality Planner

Hobby: Listening to music, Orienteering, Knapping, Dance, Mountain biking, Fishing, Pottery

Introduction: My name is Duane Harber, I am a modern, clever, handsome, fair, agreeable, inexpensive, beautiful person who loves writing and wants to share my knowledge and understanding with you.