Implement Dark Mode with Zustand and Tailwind CSS in React

The Dark Side Of The Force Is A Pathway To Many Abilities Some Consider To Be Unnatural — Darth Plagueis

Rohit Kashyap
Nerd For Tech
5 min readMar 14, 2021

--

It’s time to embrace the dark side.

Today, I’ll show you how to create a dark mode react application with zustand (german) and Tailwind CSS. Let’s begin!

First off, what’s zustand?

Zustand is a small, fast and scaleable bearbones state-management solution. Has a comfy api based on hooks, isn’t boilerplatey or opinionated, but still just enough to be explicit and flux-like.

If you are familiar with redux, this should be a piece of cake for you.

I am going to use create-react-app for this application. Fire up your terminal and run the following command

Before we start with the cool stuff, we need to install our dependencies.

Run npm install zustand to install zustand.

Setting up tailwind with create-react-app is already documented in the official tailwind documentation. Navigate to this page to set up tailwind CSS with our project.

Once you are done, let’s begin!

Step 1: Setting up Tailwind Config 🔨

First, we need to set up some tailwindconfig. Navigate to tailwind.config.js inside your project root. If you followed the steps here correctly, there should already be the file there for you.

Add the following to your tailwind.config.js :

Notice the darkMode key, this is straight from the tailwind docs. Adding class as the value allows us to manually control the dark mode. You can also use media to toggle themes based on your OS theme.

Let’s move on.

Step 2: Building the UI 🖥️

Open App.js and replace the content as below:

Let’s discuss what’s happening here. With Tailwind, all we need to do to implement dark mode styles is to add the dark prefix. For example:

dark:bg-black dark:text-white

If you are familiar with tailwind, this concept won’t seem alien to you. The styles after the dark: prefix will only be applied when your application is in dark mode. You can read about it more here.

Step 3: Constants ⚙

Before we move on to our toggle function, let’s declare our themes in a constants file. Create a new folder called constants inside your src directory. We’ll call it index.js . Place the following code inside src/constants/index.js

This will help avoid typos throughout our codebase.
Now, we need a way to toggle our dark mode. With tailwind, we need to add class dark to our root element.

Step 4: Writing our Toggle Function 🌗

Now, let’s move to our toggle function.

Let’s discuss what's happening here. Our applyThemePreference function accepts the current theme as a parameter. Based on the current theme, we can add/remove the dark attribute from our root element.

That is if the current theme is light , this will add a dark class to our root element and vice-versa. Now, we need a way to persist our theme so even if we reload the page, our theme of preference should persist. Enter zustand.

Zustand comes with a built-in middleware that allows us to persist our state to localStorage . Let’s build our theme store.

Step 5: Manage Global State with Zustand 💪

Navigate to src and create a new folder called stores . Inside stores , we will create a new file called useThemeStore . Replace the contents src/stores/useThemeStore with the following:

Let’s discuss what’s happening here. We have our themeStore that exposes the current theme of our application and a function to toggle the theme. This will change our theme variable from dark to light and vice-versa depending on the current theme of our application.

The persist middleware will allow our state to be saved in localStoragewith the key theme . We can pass a configuration object to our persist middleware to change the name of the localStoragekey and other attributes.

Step 6: Say Hello to the dark side! 🌚

Head back to App.js and modify it as below:

Let’s discuss what’s happening here.

Zustand exposes a hook to consume that state into our component. If you remember, we stored the current theme and toggleTheme inside our state. We can now consume the state using our useThemeStore hook.

We pass a selector our hook that extracts those values for us. You can declare your selectors outside the component to prevent unnecessary calculations at each render. But that’s a story for another article.

Here, we get our theme and toggleTheme inside our component which we can use to manage the theme state of our application. We pass the toggleTheme to our button onClick.This will take care of changing the theme for the global state.

Now, to change the theme and reflect on your UI, we need to use our applyThemePreference function coupled with our useEffect .

Why do we need a useEffect here, you ask? Well, we need to add/remove the dark class from our root element every time the theme changes.

Click on the Toggle Theme button to see the magic! 🧙

And…. we are done!!! ​🥳​🎉​​💃🏻​

But wait, we can do a little more. We can extract this functionality into a custom-hook.

Create a new folder hooks inside src and name it useTheme.js . Inside your src/hooks/useTheme.js , add the following code:

Inside your App.js , you have a reusable hook!

There you go, we are done!!!!

Neat!

You can view more of my work at Github and follow me on Twitter if you are interested.

Thanks for reading!!

--

--

Rohit Kashyap
Nerd For Tech

Engineer at Falabella India | JavaScript Enthusiast