DEV Community

Khan Rabiul
Khan Rabiul

Posted on

1

Tailwind CSS V4 Dark Mode

How to implement theme toggler in tailwind CSS v4 in React project

Theme handler

First, we need a button or a component that will toggle dark and light mode.
Into the component, we need useState to hold the context's current value.

Create the Theme Controller component

Button.jsx

const DarkModeToggler = () => {
return (
<>
<button>
{darkMode ? "Dark" : "Light" }
</button>
)
};
Enter fullscreen mode Exit fullscreen mode

Use in CSS file. The file could be index.css or global.css or something else.

@import "tailwindcss";
@custom-variant dark (&:where(.dark, .dark *));
Enter fullscreen mode Exit fullscreen mode

Create Context

Now create a context. It should look like this src/context/ThemeContext.jsx. Into the ThemeContext.jsx component we will implement the react context's logic.

import {createContext, useEffect, useState } from "react";

const ThemeContext = createContext('light');

const ThemeProvider = ({children}) => {
const [darkMode, setDarkMode] = useState(
localStorage.getItem("theme") === "dark"
)

return(
<ThemeContext.Provider value={{darkMode,setDarkMode}}>
{children}
</ThemeContext.Provider>
)

}

Enter fullscreen mode Exit fullscreen mode

Here we can use hard-coded useStatelogic. Like const [darkMode, setDarkMode] = useState('light'). This will also work. But when a user refreshes the page the useState value will be re-seted. To prevent this issue we used localStorage, which is the current value for darkMode.To know when to use dark mode and when to use light mode, we need to use useEffect.

useEffect(() => {
if(darkMode) {
document.documentElement.classList.add("dark");
localStorage.setItem("theme", "dark");
} else { 
  document.documentElement.classList.remove("dark");
loacalStorage.setItem("theme", "light");
}
}, 
[darkMode]) 
Enter fullscreen mode Exit fullscreen mode

Here,

import { createContext, useContext, useEffect, useState } from "react";

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => { 
  const [darkMode, setDarkMode] = useState(
    localStorage.getItem("theme") === "dark"
  );

  useEffect(() => {
    if (darkMode) {
      document.documentElement.classList.add("dark");
      localStorage.setItem("theme", "dark");
    } else { 
      document.documentElement.classList.remove("dark");
      localStorage.setItem("theme", "light");
    }
  }, [darkMode]);

  return (
    <ThemeContext.Provider value={{ darkMode, setDarkMode }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useDarkMode = () => useContext(ThemeContext);

Enter fullscreen mode Exit fullscreen mode

Export ThemeContext

export const useDarkMode = () => useContext(ThemeContext)
Enter fullscreen mode Exit fullscreen mode

NB: Don't forget to export the component export const ThemeProvider = () => {...}

Here is the final code for ThemeContext.jsx

import { createContext, useContext, useState, useEffect } from "react";

const ThemeContext = createContext("light");

export const ThemeProvider = ({ children }) => {
  const [darkMode, setDarkMode] = useState(
    localStorage.getItem("theme") === "dark"
  );

  useEffect(() => {
    if (darkMode) {
      document.documentElement.classList.add("dark");
      localStorage.setItem("theme", "dark");
    } else {
      document.documentElement.classList.remove("dark");
      localStorage.setItem("theme", "light");
    }
  }, [darkMode]);

  return (
    <ThemeContext.Provider value={{ darkMode, setDarkMode }}>
      {children}
    </ThemeContext.Provider>
  );
};

export const useDarkMode = () => useContext(ThemeContext);

Enter fullscreen mode Exit fullscreen mode

Now we are ready to use controlling them.
To implement we have to go to the root component.main.jsx and wrap it with <ThemeProvider> ...</ThemeProvider>

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import ThemeProvider from "./context/ThemeContext.jsx";
import "./index.css";

ReactDOM.createRoot(document.getElementById("root")).render(
  <ThemeProvider>
    <App />
  </ThemeProvider>
);

Enter fullscreen mode Exit fullscreen mode

Change the state in the toggler component(DarkModeToggler.jsx) and import state from ThemeContext. To toggle them we need an onClick and a handlerfunction The component should look like this.

import { useContext } from "react";
import { ThemeContext } from "../context/ThemeContext";

const DarkModeToggler = () => {
  const { darkMode, setDarkMode } = useContext(ThemeContext);

  return (
    <button
      onClick={() => setDarkMode((prev) => !prev)}
      className="p-2 border rounded"
    >
      {darkMode ? "Light Mode" : "Dark Mode"}
    </button>
  );
};

export default DarkModeToggler;

Enter fullscreen mode Exit fullscreen mode

Now we are ready to use dark and light mode in our application.

Here is a simple example of how to use the theme.

import { useDarkMode } from "../context/ThemeContext";

const Card = () => {
  const { darkMode } = useDarkMode();

  return (
    <div className="p-5 rounded-lg shadow-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
      <h2 className="text-xl font-bold">Dark Mode Card</h2>
      <p className="mt-2">This card changes theme based on the mode.</p>
      <button className="mt-4 px-4 py-2 bg-blue-500 dark:bg-blue-700 text-white rounded-lg">
        Click Me
      </button>
    </div>
  );
};

export default Card;

Enter fullscreen mode Exit fullscreen mode

Heroku

Built for developers, by developers.

Whether you're building a simple prototype or a business-critical product, Heroku's fully-managed platform gives you the simplest path to delivering apps quickly — using the tools and languages you already love!

Learn More

Top comments (0)

Image of Stellar post

Check out Episode 1: How a Hackathon Project Became a Web3 Startup 🚀

Ever wondered what it takes to build a web3 startup from scratch? In the Stellar Dev Diaries series, we follow the journey of a team of developers building on the Stellar Network as they go from hackathon win to getting funded and launching on mainnet.

Read more

👋 Kindness is contagious

Dive into this insightful write-up, celebrated within the collaborative DEV Community. Developers at any stage are invited to contribute and elevate our shared skills.

A simple "thank you" can boost someone’s spirits—leave your kudos in the comments!

On DEV, exchanging ideas fuels progress and deepens our connections. If this post helped you, a brief note of thanks goes a long way.

Okay