A simple way of adding dark mode to your React app using Hooks and saving it to the local storage
Tags: webdev, react, javascript, beginners
Published: 2020-10-14 11:06:52


In this article I will share my approach for the dark/light mode toggle that I recently implemented in a [React project](http://leila-tomato-timer.netlify.com/), which I think is quite easy to understand also for beginners.

![This is how it looks in my application](https://dev-to-uploads.s3.amazonaws.com/i/rtjkwygm1gbbqs2g34c3.gif)

First I add a `<span>` element into my `App.tsx` file. It can either be a `<button>`, `<div>`, whatever you prefer. This will act as a switch for dark/light mode : 

```jsx
import React, { useEffect, useState } from 'react';

function App() {
    return (
      <div className='container'>
        <span className='mode-switch'></span>
        {/* my other elements */}
      </div>
    )
}

export default App;

```


Then I add some basic styles. I prefer my switch to be positioned absolutely on the right top corner of my container element:

```scss
.container {
  position: relative;
  max-width: 1400px;
  padding: 40px 30px;
}

.mode-switch {
  position: absolute;
  right: 15px;
  top: 15px;
  font-size: 11px;
  cursor: pointer;
  transition: color 0.2s ease-in-out;

  &:hover {
    color: #50bbf1;
  }
}

```

I go back to my `App` component and add the **`useState`** hook. I define a `mode` variable and a `setMode` function. For now I pass the default mode as 'light' inside the `useState` hook.
Then I add an `onClick` event to my switch and in this event, I call the `setMode` function with a conditional parameter. 
This function makes sure that it sets the mode to dark if it was light, and vice versa.
I also add the text content into the switch dynamically :

```jsx
function App() {
    const [mode, setMode] = useState('light'); 

    return (
      <div className='container'>
        <span 
           className='mode-switch'
           onClick={() => 
             setMode(mode === 'dark' ? 'light' : 'dark')
           }
        >
           {mode === 'dark' ? 'Light mode' : 'Dark mode'}
        </span>
      </div>
    )
}

``` 

Next step is switching between modes and adding/removing relevant styles, which will be achieved using the **`useEffect`** hook. 
It will simply add a '.dark' class to the `<body>` when switched to the dark mode, and remove it when the selected mode is light. 
I pass `[mode]` as the second parameter to `useEffect` because it will work as the **side effect** of the changing 'mode':

```jsx
function App() {
    const [mode, setMode] = useState('light'); 

    useEffect(() => {
      if (mode === 'dark') {
        document.body.classList.add('dark');
      } else {
        document.body.classList.remove('dark');
      }
    }, [mode]);

    return (
      
```

Then I add the necessary styles, which make the background-color black and turn all the text to white if they were not originally assigned any color and were black by default:

```scss
.dark {
  background-color: #222;
  color: #f5f5f5; 
}

```

In order to style other elements other than the `<body>` in dark mode, I use the `&` selector.
Let's say I have a button with 'primary-button' class. I want to change its color and background-color when the dark mode is active:

```scss
.primary-button {
  // default style: black button with white text
  background-color: #222;
  color: #f5f5f5;

  // dark mode style: white button with black text 
  .dark & {
    background-color: #f5f5f5;
    color: #222;
  }
}

```

Now it's time to save the selected mode to the **local storage**, so that the selected mode will persist even if the app is reset. To achieve this, first I go back to the `useEffect` hook and include the following code into it:

```jsx
useEffect(() => {
  if (mode === 'dark') {
     document.body.classList.add('dark');
  } else {
     document.body.classList.remove('dark');
  }
  localStorage.setItem('mode', mode); // mode saved to local storage
}, [mode]);

```

Then I go up and create a utility function called `getDefaultMode` on a **global level**. This function will get the saved mode from the local storage and determine the default mode accordingly when the app starts. If dark mode was not selected previously, the default mode will be 'light':

```jsx
function getDefaultMode() {
  const savedMode = localStorage.getItem('mode');
  return savedMode ? savedMode : 'light';
}

```

Now I need to call this function inside the `useState` hook that I previously added inside my `App` component. I replace the `light` parameter with the `getDefaultMode` function: 

```jsx
const [mode, setMode] = useState(getDefaultMode());
```

The final code looks like this in the end:

```jsx
import React, { useEffect, useState } from 'react';

function getDefaultMode() {
  const savedMode = localStorage.getItem('mode');
  return savedMode ? savedMode : 'light';
}

function App() {
    const [mode, setMode] = useState(getDefaultMode()); 

    useEffect(() => {
      if (mode === 'dark') {
        document.body.classList.add('dark');
      } else {
        document.body.classList.remove('dark');
      }
      localStorage.setItem('mode', mode);
    }, [mode]);

    return (
      <div className='container'>
        <span 
           className='mode-switch'
           onClick={() => 
             setMode(mode === 'dark' ? 'light' : 'dark')
           }
        >
           {mode === 'dark' ? 'Light mode' : 'Dark mode'}
        </span>
        {/* my other elements */}
      </div>
    )
}

```