DEV Community

Shawn(Heongi) Yeo
Shawn(Heongi) Yeo

Posted on • Updated on

Dark Mode Tailwind & React (No flashing!)

Initial Set up

First Lets set up our initial Project.

git clone https://github.com/hyeo151/Darkmode-Tutorial-React-Tailwind.git

cd Darkmode-Tutorial-React-Tailwind

npm install

npm run dev
Enter fullscreen mode Exit fullscreen mode

When you start your server,you will either see light mode or dark mode depending on your OS color preference.

Light Mode
Light Mode Preview
Dark Mode
Dark Mode Preview

By default tailwind uses prefers-color-scheme CSS media feature to enable Dark mode.

Class Strategy Set up

If we wish to add Dark mode which can be toggled by a user we can use class strategy

Go to tailwind.config.js and add darkMode: 'class'

/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  darkMode: 'class',
  theme: {
    extend: {},
  },
  plugins: [],
};

Enter fullscreen mode Exit fullscreen mode

Now we can add darkmode by simply adding class="dark" in our html element in index.html like this,

enables dark mode
<html lang="en" class="dark">

disable dark mode
<html lang="en" class="">

Dark Mode Logic

Now that we have set up class Strategy in our project let's add our logic for dark mode.

First we will add following script in our index.html in head element. Add this script before any other scripts or links.

<script>
      const initialTheme =
        localStorage.theme === "dark" ||
        (!("theme" in localStorage) &&
          window.matchMedia("(prefers-color-scheme: dark)").matches);
      if (initialTheme) {
        window.document.documentElement.classList.add("dark");
      }
</script>
Enter fullscreen mode Exit fullscreen mode

Script like this is called Render-blocking resources which prevents a web page from loading quickly. However we use this method as opposed to using useEffect hook to avoid flashing

This Script instruct following,

[if local storage theme variable is dark]
OR
[if theme variable doesn't exist in local storage AND OS preference is dark]
THEN set our page as dark mode.

This script will load correct mode on initial page load.

Now Let's add darkTheme state for our project.
Go to App.jsx and declare darkTheme state.
We can use initialTheme that we declared previously and use it as our initial state.

const [darkTheme, setDarkTheme] = useState(initialTheme);
Enter fullscreen mode Exit fullscreen mode

and we show icon according to our darkTheme state

{darkTheme ? (
  <i className="fa-solid fa-sun"></i>
) : (
  <i className="fa-regular fa-moon"></i>
)}
Enter fullscreen mode Exit fullscreen mode

then we add clicked button which to toggle this state.

onClick={() => {
  handleThemeSwitch(setDarkTheme);
}}
Enter fullscreen mode Exit fullscreen mode

Define function handleThemeSwitch

const handleThemeSwitch = (setDarkTheme) => {
  if (document.documentElement.classList.contains("dark")) {
    document.documentElement.classList.remove("dark");
    localStorage.theme = "light";
    setDarkTheme(false);
    return;
  }
  localStorage.theme = "dark";
  document.documentElement.classList.add("dark");
  setDarkTheme(true);
};
Enter fullscreen mode Exit fullscreen mode

Final Code will look like this,

import { useState } from "react";
import "./App.css";

const handleThemeSwitch = (setDarkTheme) => {
  if (document.documentElement.classList.contains("dark")) {
    document.documentElement.classList.remove("dark");
    localStorage.theme = "light";
    setDarkTheme(false);
    return;
  }
  localStorage.theme = "dark";
  document.documentElement.classList.add("dark");
  setDarkTheme(true);
};

function App() {
  const [darkTheme, setDarkTheme] = useState(initialTheme);
  return (
    <div className="max-w-md rounded-md bg-slate-200 p-5 dark:bg-slate-300/80">
      <h1 className="mb-2 text-xl font-bold">Tailwind Dark/Light Mode</h1>
      <p className="mb-4">
        In this tutorial we will be learning how to enable dark mode for your
        tailwind react website/app
      </p>
      <button
        className="rounded-md bg-slate-500 px-5 py-1 font-bold"
        onClick={() => {
          handleThemeSwitch(setDarkTheme);
        }}
      >
        <div className="moon">
          {darkTheme ? (
            <i className="fa-solid fa-sun"></i>
          ) : (
            <i className="fa-regular fa-moon"></i>
          )}
        </div>
      </button>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Now whenever user click on the switch button we can switch the dark mode.

If you get stuck you can check out the solution branch

git checkout solution 
Enter fullscreen mode Exit fullscreen mode

Thank you for reading!

If you liked this post please like, comment and share.
Ask questions in comment section : )

Top comments (0)