Looking to enhance your website’s accessibility and modernity by adding a dark mode feature? Follow this comprehensive guide on implementing dark mode in React, complete with best practices.
Step 1: Create React app using Vite (skip if already created)
Begin by setting up the React App using Vite, a popular build tool known for simplifying and expediting the development process. Use the following command to initiate the project:
yarn create vite
Vite will prompt you to specify a Project name, select a framework, and choose a variant. For this guide, choose “react” as the framework and “react-ts” as the variant. Once your app is set up, navigate to the project, install dependencies, and you’re ready to proceed.
Step 2: Create theme context
In the /src/contexts folder, open a theme-context.ts file and create the theme context.
// theme-context.ts
import { createContext } from 'react';
export const ThemeContext = createContext({
theme: '',
setTheme: (theme: string) => {},
});
This context will receive “light” or “dark” as input and return the same as output.
Step 3: Create theme-variables.scss file with variables for the light and dark themes
Within the src folder, create a styles folder and a theme-variables.scss file. This file will contain color alternatives for light and dark modes.
// theme-variables.scss
$themes: (
light: (
colorHeaderFooter: #fff,
colorText: #000,
colorBackground: #f8f8fa,
),
dark: (
colorHeaderFooter: #36394c,
colorText: #eff2f7,
colorBackground: #222736,
),
);
Continue by creating a mixin named themify to generate dark and light themes. Also, create a function called themed to retrieve 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); }
Step 4: Wrap the App component in the theme context
In the App.tsx file, import the Theme context created in Step 2.
// App.tsx
import { ThemeContext } from './contexts/theme-context';
Create a state for the current theme, initially set to “light,” and wrap the entire component within the Theme provider.
// App.tsx
const [theme, setTheme] = useState('light');
<ThemeContext.Provider value={{ theme, setTheme }}>
<div className={`theme-${theme}`}>
<Layout>
{/* Your code here */}
</Layout>
</div>
</ThemeContext.Provider>
This context provider will provide the current theme name, used in the className of the theme wrapper.
Step 5: Create a layout component with a light/dark mode toggle button
Create a simple toggle button to switch between themes in a new Header.tsx file.
// Header.tsx
import { 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">
{/* Header content */}
<div className="toggle-btn-section">
{/* Toggle button */}
<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>
</header>
);
};
export default Header;
This toggle button changes the theme on click. The theme value is stored in localStorage for persistence.
Step 6: Detect the browser’s default mode if any to set up the default theme
In App.tsx, set the theme’s default value based on the browser’s default mode using matchMedia.
// App.tsx
const isBrowserDefaultDark = () => window.matchMedia('(prefers-color-scheme: dark)').matches;
const [theme, setTheme] = useState(isBrowserDefaultDark() ? 'dark' : 'light');
Step 7: Set the default theme in the localStorage
Save the theme in localStorage in the Header.tsx file, adding the following line to the handleThemeChange function.
// Header.tsx
localStorage.setItem('theme', isCurrentDark ? 'light' : 'dark');
Now, the default theme is retrieved from localStorage whenever a user enters the website.
With these steps, your dark mode in react app is ready for use.
If you liked this blog, please share it with your friends and colleagues. Connect with FE Competency on LinkedIn to read more about such topics.