Published at: October 24, 2021
Dark Mode is no longer just a trendy feature – it’s an essential option for many users. Whether it’s to reduce eye strain or to simply match personal preferences, implementing Dark Mode in a React app has become a must-have feature. In this post, we’ll go over how to build a dark mode switcher in React using useContext, and style it beautifully with Tailwind CSS. Plus, we’ll store users’ theme preferences in local storage, so their choice persists across sessions.
1. Why Dark Mode?
Dark Mode isn’t just about aesthetics; it’s about accessibility and improving the overall user experience. It’s an effective way to reduce eye strain, particularly when using apps at night or in low-light conditions. As more apps and websites offer this option, it has become a critical feature for modern apps.
2. Setting Up Tailwind CSS
Before we dive into building the Dark Mode switcher, let’s first ensure that Tailwind CSS is set up in your React app. If you haven’t already done so, follow these steps:
Step 1: Install Tailwind CSS
Start by installing Tailwind CSS with the following steps:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
This will create a tailwind.config.js
file. Next, create a postcss.config.js
file:
touch postcss.config.js
Then add the following configuration:
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Step 2: Set Up Tailwind in Your CSS
Open the src/index.css
file (or create one if it doesn’t exist) and include the following base Tailwind CSS imports:
@tailwind base;
@tailwind components;
@tailwind utilities;
Tailwind is now set up, and you can use its utility classes to style your app.
3. Building the Theme Switcher with Context
The first step in building a Dark Mode switcher is to manage the theme state. We’ll use React Context to store the current theme and make it accessible across the entire app.
Step 1: Create a ThemeContext
Create a ThemeContext.js
file in your src
folder:
// src/ThemeContext.js
import React, { createContext, useState, useEffect } from 'react';
export const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
// Get the theme from localStorage or default to 'light'
const savedTheme = localStorage.getItem('theme') || 'light';
// Set the initial theme state
const [theme, setTheme] = useState(savedTheme);
// Update localStorage whenever the theme changes
useEffect(() => {
localStorage.setItem('theme', theme);
document.documentElement.className = theme; // Apply theme to the <html> tag for global styles
}, [theme]);
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
Step 2: Wrapping Your Application with ThemeProvider
Now, open your src/index.js
file and wrap your entire app with the ThemeProvider
:
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ThemeProvider } from './ThemeContext'; // Import ThemeProvider
ReactDOM.render(
<ThemeProvider>
<App />
</ThemeProvider>,
document.getElementById('root')
);
4. Building the Theme Switcher Button
Next, let’s create a simple button that toggles between light and dark modes. You can use a simple button or a toggle switch to allow the user to change the theme.
Step 1: Create the Button Component
Create a ThemeSwitcher.js
component:
// src/ThemeSwitcher.js
import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
const ThemeSwitcher = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<button
onClick={toggleTheme}
className="p-2 bg-blue-500 text-white rounded-md"
>
Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode
</button>
);
};
export default ThemeSwitcher;
5. Styling with Tailwind CSS
Tailwind makes it easy to add styles to our app. Let’s define some global styles that change based on the theme:
Step 1: Add Tailwind Classes for Themes
Modify your src/index.css
or global stylesheet to handle the light and dark modes:
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* Light Mode */
html.light {
--bg-color: #ffffff;
--text-color: #000000;
}
/* Dark Mode */
html.dark {
--bg-color: #121212;
--text-color: #ffffff;
}
/* Apply dynamic styles */
body {
background-color: var(--bg-color);
color: var(--text-color);
}
Now, based on whether the user selects “Light” or “Dark”, the appropriate classes will be applied to the root <html>
element, and the styles will adjust accordingly.
6. Persisting Theme Preferences with Local Storage
We’re using localStorage
in the ThemeContext
to store the user’s theme choice. This ensures that the user’s preferred theme persists even after they refresh the page or close the browser.
In the ThemeProvider
, we’re getting the theme from localStorage
when the app loads:
const savedTheme = localStorage.getItem('theme') || 'light';
Then, when the user toggles the theme, we update both the React state and localStorage
:
useEffect(() => {
localStorage.setItem('theme', theme);
document.documentElement.className = theme; // Apply theme to <html>
}, [theme]);
7. Putting It All Together
Finally, you can use the ThemeSwitcher
component in your main App.js
:
// src/App.js
import React from 'react';
import ThemeSwitcher from './ThemeSwitcher';
const App = () => {
return (
<div className="min-h-screen flex items-center justify-center">
<h1 className="text-3xl font-bold">React Dark Mode</h1>
<ThemeSwitcher />
</div>
);
};
export default App;
8. Conclusion
In this post, we’ve built a simple Dark Mode switcher in React using useContext to manage the theme state, Tailwind CSS to style the app, and localStorage to persist the theme preference across sessions. With this approach, users will enjoy a seamless and customizable experience while using your app. By leveraging modern React patterns and Tailwind’s utility-first CSS, we’ve created a maintainable and scalable solution.