Modern Theming in React: Dark Mode, Tailwind, and Context API

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:

  1. Reduced Eye Strain: Dark backgrounds with light text are easier on the eyes, especially in low-light environments.
  2. Battery Saving: On OLED screens, dark mode can save battery life since dark pixels consume less power.
  3. 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.

  1. Install Tailwind CSS and the necessary dependencies:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init
  1. 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: [],
}
  1. 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.

  1. First, create a ThemeContext.js file in your src 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 and dark modes. It updates the state and also stores the preference in localStorage so the theme persists across sessions.
  • useEffect: When the component loads, we check the saved theme and apply the appropriate classes (dark or light) 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.

  1. 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 or Dark) based on the theme 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.

  1. Now that the theme context is set up, let’s use these classes in the app. Update your App.js to include the ThemeProvider and ThemeSwitcher.
// 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 and dark:bg-gray-800 for dark mode) and the text color (text-gray-900 for light mode and dark: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.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *