The Easiest Way to Implement Theme Toggling in React 19 using next-themes & Tailwind CSS v4

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • MyrinNew
    Senior Member
    • Feb 2024
    • 5168

    #1

    The Easiest Way to Implement Theme Toggling in React 19 using next-themes & Tailwind CSS v4

    There is almost no React project today that doesn’t need at least two themes (Light and Dark). While you could build a custom theme toggle using the React Context API, it usually leads to complex boilerplate code just to handle system preferences and saving user choices.


    Instead, we can use the incredibly popular NPM package next-themes. Despite the name, it works beautifully with plain React! While Next.js requires a bit more care with theming due to Server-Side Rendering (SSR) and hydration mismatches, setting this up in a React Single Page Application (SPA) is incredibly straightforward.


    In this article, I will show you how to implement a seamless theme toggle using React 19, Vite, and the newly released Tailwind CSS v4.


    Step 1: Create a New Vite Project

    First things first, let’s create a new React project using Vite. Open your terminal and run:






    pnpm create vite
    # OR
    npm create vite







    Follow the prompts to select React and JavaScript/TypeScript. Once that’s done, install and configure Tailwind CSS v4 according to their official documentation.


    Step 2: Configure Tailwind CSS v4 for Dark Mode

    Tailwind v4 is CSS-first, meaning we handle configuration right inside our CSS file. To enable class-based dark mode, we just need to define a custom variant.


    Open your ./src/index.css file and set it up like this:






    /* ./src/index.css */
    @import "tailwindcss";
    /* Add this line to enable class-based dark mode */
    @custom-variant dark (&:where(.dark, .dark *));







    Step 3: Install Dependencies

    Next, let’s install next-themes and a library for our theme icons (react-icons):






    pnpm add next-themes react-icons
    # OR
    npm install next-themes react-icons







    Step 4: Create the Theme Provider

    Now, let’s create a wrapper component for our theme. This provider will wrap our entire application and inject the current theme into the HTML structure.


    Create a new file called ReactThemeProvider.jsx:






    // ./src/ReactThemeProvider.jsx
    import { ThemeProvider } from "next-themes";
    export default function ReactThemeProvider({ children }) {
    return (
    ThemeProvider attribute="class" defaultTheme="system" enableSystem>
    {children}
    /ThemeProvider>
    );
    }







    Note: By settingattribute="class" , we are tellingnext-themes to toggle thedark class on the element, which works perfectly with our Tailwind configuration.


    Step 5: Wrap Your App Component

    Head over to your entry file and wrap the component with the provider we just created.






    // ./src/main.jsx
    import { StrictMode } from "react";
    import { createRoot } from "react-dom/client";
    import "./index.css";
    import App from "./App.jsx";
    import ReactThemeProvider from "./ReactThemeProvider.jsx";

    createRoot(document.getElementById("root")).render (
    StrictMode>
    // wrap App component with ReactThemeProvider
    ReactThemeProvider>
    App />
    /ReactThemeProvider>
    /StrictMode>
    );







    Step 6: Create the Toggle Button

    Finally, let’s create the button that users will click to toggle the theme. We will use the useTheme hook provided by next-themes to check the resolvedTheme (which calculates whether the system preference is currently light or dark) and switch it.






    // ./components/ToggleThemeBtn.jsx
    import { useTheme } from "next-themes";
    import { LuSun, LuMoon } from "react-icons/lu";

    export function ToggleThemeBtn() {
    const { resolvedTheme, setTheme } = useTheme();
    return (
    button
    onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
    className="p-2 rounded-md bg-slate-200 dark:bg-slate-800 text-slate-800 dark:text-slate-200 transition-colors"
    aria-label="Toggle theme"
    >
    {resolvedTheme === "dark" ? LuSun size={20} /> : {20} />}
    /button>
    );
    }







    And that is it! You now have a fully functioning, Tailwind-compatible theme toggle that respects user system preferences and saves their choices locally. Easy, right?!




    More...
Working...