Published: 21 October 2023
Introduction
The demand for modern and adaptable user interfaces is ever-increasing. One essential feature that users expect today is dark mode, which allows them to switch between light and dark themes according to their preference. This functionality not only enhances the user experience but also provides a more accessible UI for different lighting conditions.
In this blog post, we’ll explore how to implement a dark mode switcher in a React app using the Context API and Tailwind CSS for styling. The combination of these two tools allows for efficient, reusable code that is easy to manage. Additionally, we’ll make sure our theme persists across sessions by storing the user’s preference in localStorage.
Why Dark Mode?
Dark mode isn’t just a trendy feature—it’s a design pattern that provides several benefits:
- Reduced Eye Strain: Dark backgrounds with light text are easier on the eyes, especially in low-light environments.
- Battery Saving: On OLED screens, dark mode can save battery life since dark pixels consume less power.
- Aesthetic Appeal: Many users simply prefer dark mode for its sleek, modern look.
Step 1: Setting Up Tailwind CSS in Your React App
If you don’t already have Tailwind set up, you’ll first need to integrate it into your React project.
- Install Tailwind CSS and the necessary dependencies:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
- Add the following to your
tailwind.config.js
file to enable dark mode:
module.exports = {
darkMode: 'class', // This enables class-based dark mode
content: [
'./src/**/*.{html,js,jsx,ts,tsx}', // Add your file paths here
],
theme: {
extend: {},
},
plugins: [],
}
- Next, add the Tailwind directives to your
src/index.css
file:
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 2: Creating the Theme Context
The Context API in React allows us to pass data through the component tree without having to manually pass props down to every level. This is ideal for managing global state, like the theme preference in our case.
- First, create a
ThemeContext.js
file in yoursrc
folder to manage the theme state.
// src/context/ThemeContext.js
import React, { createContext, useState, useEffect, useContext } from 'react';
const ThemeContext = createContext();
export const useTheme = () => useContext(ThemeContext);
export const ThemeProvider = ({ children }) => {
// Initialize the theme state, defaulting to light mode
const [theme, setTheme] = useState(() => {
// Check if there's a saved theme in localStorage
const savedTheme = localStorage.getItem('theme');
return savedTheme ? savedTheme : 'light'; // Default to 'light'
});
// Update the theme in localStorage and the document class
const toggleTheme = () => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
document.documentElement.classList.toggle('dark', newTheme === 'dark');
};
useEffect(() => {
// Initially apply the theme when the app loads
document.documentElement.classList.toggle('dark', theme === 'dark');
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
Explanation:
- ThemeContext: This is a context that holds the current theme and the function to toggle the theme.
- useTheme: A custom hook to easily access the current theme and the
toggleTheme
function throughout the app. - toggleTheme: This function toggles between
light
anddark
modes. It updates the state and also stores the preference inlocalStorage
so the theme persists across sessions. - useEffect: When the component loads, we check the saved theme and apply the appropriate classes (
dark
orlight
) to the<html>
element.
Step 3: Adding the Theme Switcher
Now that we have the context set up, let’s create a simple Theme Switcher component that allows users to toggle between light and dark modes.
- Create a
ThemeSwitcher.js
component:
// src/components/ThemeSwitcher.js
import React from 'react';
import { useTheme } from '../context/ThemeContext';
const ThemeSwitcher = () => {
const { theme, toggleTheme } = useTheme();
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;
Explanation:
- This button simply calls the
toggleTheme
function when clicked, and displays the current theme (Light
orDark
) based on thetheme
state. - The button is styled using Tailwind CSS for a quick, modern look.
Step 4: Applying Tailwind’s Dark Mode Classes
In Tailwind CSS, you can conditionally apply classes based on the theme mode using the dark:
prefix.
- Now that the theme context is set up, let’s use these classes in the app. Update your
App.js
to include theThemeProvider
andThemeSwitcher
.
// src/App.js
import React from 'react';
import { ThemeProvider } from './context/ThemeContext';
import ThemeSwitcher from './components/ThemeSwitcher';
const App = () => {
return (
<ThemeProvider>
<div className="min-h-screen flex flex-col items-center justify-center bg-white dark:bg-gray-800 text-gray-900 dark:text-white">
<h1 className="text-4xl font-bold mb-4">React Dark Mode Example</h1>
<ThemeSwitcher />
</div>
</ThemeProvider>
);
};
export default App;
Explanation:
- The
ThemeProvider
component wraps the entire app, allowing the theme state to be accessible throughout. - We’re using Tailwind’s
dark:
variant to apply different styles depending on the current theme. For example, we change the background color (bg-white
for light mode anddark:bg-gray-800
for dark mode) and the text color (text-gray-900
for light mode anddark:text-white
for dark mode).
Step 5: Testing the Theme Switcher
Now that everything is set up, you can run your app, and you should see:
- A button to switch between Light Mode and Dark Mode.
- When the button is clicked, the theme will switch, and the preference will be saved in localStorage.
- The theme will persist even after reloading the page.
Conclusion
In this post, we’ve learned how to build a dark mode switcher in React using the Context API and Tailwind CSS. By combining these tools, we were able to create a seamless and efficient theming solution that is easy to implement and highly customizable.
We covered:
- Setting up Tailwind CSS in a React app for theme-based styling.
- Using React Context API to manage the global theme state.
- Storing theme preferences in localStorage to ensure persistence.
- Creating a user-friendly ThemeSwitcher component to toggle between light and dark modes.
This approach provides a modern, scalable way to manage themes in your React applications and enhances the user experience by allowing them to choose their preferred mode.