DEV Community

Cover image for No Fuss Dark-Mode Toggle with React & Styled-Components! 🌞↔️🌖
Chris Hansen
Chris Hansen

Posted on • Updated on

No Fuss Dark-Mode Toggle with React & Styled-Components! 🌞↔️🌖

Have you noticed dark mode is pretty standard lately?

Well, that's because it's badass!

Let's achieve this new standard of awesome by utilizing styled-components and React!

It's super easy to implement, so let's get straight to it! 💯


Resources

👉 Demo

👉 Repo


Some things I'm expecting you to know

  1. Development Environment
  2. Basics in React
  3. Basic S(C)ass

1. Fresh React project

Let's clean up the file structure ~ if you want. I deleted all unnecessary files for this project. Removing App.css and all testing related files. Be sure to remove necessary lines of code in index.js. I also cleaned up the boilerplate between the header tags.


2. Install and Import styled-components

With a fresh project, let's now install and import styled-components.

  • yarn add styled-components or
  • npm install --save styled-components

Let's now import styled-components like so:

import styled from 'styled-components'`
Enter fullscreen mode Exit fullscreen mode

3. Let's create a simple landing page with styled-components

In our App() let's return a Page, Container, Heading, H1, P, Toggle, and ThemeImage component, respectively. It should look like so
Example code showing the Page, Heading, H1, P, Toggle, and ThemeImage components


4. Now that we have our layout, let's create each of our components

Page and Container
Page and Container component
Heading and H1
Heading and H1
P and Toggle
P and Toggle components
ThemeImage is the component that will contain our toggle state images
ThemeImage component


5. Let's create state for our toggle component

In order for us to to toggle between light and dark mode, we need to hold state. Let's begin by importing the useState hook. import {useState} from 'react'. Then add it to your App() component like so:

const [isDarkMode, setDarkMode] = useState(false);
Enter fullscreen mode Exit fullscreen mode

Next let's add the logic that will toggle between light and dark mode for our button Toggle component.

const handleToggle = () => {
   setDarkMode(!isDarkMode);
   // console.log(isDarkMode);
}
Enter fullscreen mode Exit fullscreen mode

This functions only responsibility is to toggle the opposite of what the current state is. In other words, if the state is currently true, toggling it will become false. If false it becomes true.


6. Update our Toggle component

Now that we have a function that can toggle our light ~ dark state, let's update our Toggle component like so:

<Toggle onClick={handleToggle}></Toggle>
Enter fullscreen mode Exit fullscreen mode

7. Import images to display the active theme

If we want to show our users the current and next theme, we can do so visually with the use of a Moon and Sun image. Let's import the images at the top of our code like so:

images are available in the repo

import Moon from './images/moon.png';
import Sun from './images/sun.png';
Enter fullscreen mode Exit fullscreen mode

Then lets update our ThemeImage component like so:

<Toggle onClick={handleToggle}>
   <ThemeImage src={ !isDarkMode ? `${Sun}` : `${Moon}` } />
</Toggle>
Enter fullscreen mode Exit fullscreen mode

Here we are updating the ThemeImage components src attribute as it is an instance of img. We are also conditionally setting the image with a simple ternary operator while also utilizing template literals.
I'll explain with some pseudocode. You can read this like if stateIsNotDarkMode ? render Moon : else render Sun.


8. Let's update some style properties in our components

We're almost done! Let's update the background and color properties in a few of our components

First, Let's change the Page components background style to:

background: ${props => props.light ? "#eee" : "#333"};
Enter fullscreen mode Exit fullscreen mode

Secondly, let's change the H1 components color style to:

color: ${props => !props.light ? "papayawhip" : "#000"};
Enter fullscreen mode Exit fullscreen mode

Lastly, let's change the P components color styles to:

color: ${props => !props.light ? "#eee" : "#333"};
Enter fullscreen mode Exit fullscreen mode

Here we are conditionally styling our background or color properties based on the prop that we pass into our components. If the component contains the light prop, render this color, else render this color.


9. Pass in our default Theme to our components with the light prop

All we have to do now is update our components in the layout with the default light prop. Like so:

  return (
    <Page light={!isDarkMode ? true : false}>
      <Container>
        <Heading>
          <H1 light={!isDarkMode ? true : false}>Dynamic Styling with Styled-Components</H1>
          <P light={!isDarkMode ? true : false}>Lorem ipsum dolor sit amet consectetur adipisicing elit. Dicta error natus at vitae sint qui sapiente impedit rerum commodi fugit ullam repudiandae itaque, saepe fuga facere temporibus excepturi dolore officia?</P>
          <Toggle light={!isDarkMode ? true : false}  onClick={handleToggle}>
            <ThemeImage src={ !isDarkMode ? `${Moon}` : `${Sun}` } />
          </Toggle>
        </Heading>
      </Container>
    </Page>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here I am also conditionally rendering the light prop to either be true, or false, depending on on our state.


Conclusion! 👏

Congrats! That's a wrap on No Fuss Dark-Mode Toggle with React & Styled-Components! Hopefully, if everything went right, you were able to implement a badass dark mode into your project, in 9 simple steps!

Please don't hesitate to hit me up on Twitter in regards to any questions, concerns or if you just wanna say hello!


Are you a beginner web developer!?

Discussion (26)

Collapse
lukeshiru profile image
LUKESHIRU

One thing you can do instead of setting the default value of darkMode to false, is to actually grab it from the user's OS preferences:

const defaultDarkTheme =
    window.matchMedia?.("(prefers-color-scheme:dark)")?.matches ?? false;
const [darkMode, setDarkMode] = useState(defaultDarkTheme);
Enter fullscreen mode Exit fullscreen mode

You could also save the user preferences in localStorage.

Cheers!

Collapse
hyggedev profile image
Chris Hansen Author • Edited

That's great advice! I actually was planning on storing a userSettings in local storage but I wanted to make it as absolute barbones as possible 👌 And that's really cool. Is that the exact snippet for any OS? In other words that would take my macs theme preference and apply it to the browser? Same for a windows user? How about if my OS preferred theme is set to detect time of day still work!? 🤯

Collapse
lukeshiru profile image
LUKESHIRU

It works, even in iOS and Android :) ... Linux depends on the distro ^^

Thread Thread
hyggedev profile image
Chris Hansen Author • Edited

Amazing dude 🤘 I'ma look more into that D:

Collapse
midorun profile image
Dmitriy Vorozheykin • Edited

Hey, Chris, did you know that u have no need to pass theme prop to each component?
Look for ThemeProvider component from styled-components, it gives you opportunity to wrap you whole JSX component with provider which will pass theme prop to all styleds via context, so u will need to pass your theme only to themeProvider

Collapse
hyggedev profile image
Chris Hansen Author

Absolutely! I wanted to make this as easy as possible! But you are the second person to mention this, so I think I'm just gonna have to continue this, possibly as a series! May have to give that a shot 😅 Thanks for your input, it is good advice 🤘

Collapse
gdenn profile image
Dennis Groß

I just came to tell you how badass your caption image looks like. That is really amazing :)

Collapse
hyggedev profile image
Chris Hansen Author

Hahaha! Right on, thanks a lot! I'm super into the cyberpunk genre and it's neon colors, so I guess it worked out. 👌✌️

Collapse
gdenn profile image
Dennis Groß

Something about this caption reminded me on "Stranger Things". And I ABSOLUTELY LOVE that series. :)

Thread Thread
hyggedev profile image
Chris Hansen Author

Couldn't agree more! The production, cast... top notch. But when the show transitions into the golden age of arcades.. all that eye candy 😎

Collapse
adelekand profile image
David Adelekan • Edited

Isn't it a better approach to have a theme provider at the top level component that would take the theme object as a prop based on the value of isDarkMode, instead of having to do the comparison for each of the components. This is not sustainable for a large project that have many components.

Edit:
I wrote this comment not knowing the suggestion has already been made

Collapse
hyggedev profile image
Chris Hansen Author

Well in my honest opinion you are 💯 percent right! My aim was to basically introduce styled components, and to have the least resistance for entry for beginners. Info have a goal of updating this into a series and introducing React context, dark mode hooks, or themeprovider. Thanks for dropping by!

Collapse
kieudac201 profile image
KieuDac201

Thank you

Collapse
hyggedev profile image
Chris Hansen Author

✌️😜✌️

Collapse
moghaazi profile image
Ahmad

Awesome and smooth.

Collapse
hyggedev profile image
Chris Hansen Author • Edited

Hey 👋 Glad ya dig it! Thanks for dropping by ✌️

Collapse
andemosa profile image
Anderson Osayerie

Thank you very much for this informative article

Collapse
mateusmarquezini profile image
Mateus Marquezini

Nice post, Chris!

Collapse
hyggedev profile image
Chris Hansen Author

Hey! Thanks a lot! ✌️

Collapse
samuelnarciso28 profile image
Samuel Narciso

Thank you so much, I will to use in my blog 😄😄

Collapse
hyggedev profile image
Chris Hansen Author

That's awesome! ✌️🤘

Collapse
goodok21 profile image
Alexander Eric

Sorry, but it’s bad approach, just to pass props to each component about selected mode… at least you have to use styled theme context or css variables or whatever, but not like this

Collapse
hyggedev profile image
Chris Hansen Author

This is definitely not the end goal. For beginners who are testing the waters, this is the easiest way imo to get something up and running asap. Context and Local storage would make this much better. But as for styled-components, this totally works!

Collapse
goodok21 profile image
Alexander Eric

You want to provide context about dark mode? And get this context state in components and then pass as props again to styled components? ;) just use styled-components theme, and to persist state you can use local storage for sure 😉

Beginners need to learn good patterns - this is the most important thing, imho 🙂

Thread Thread
hyggedev profile image
Chris Hansen Author

Update coming soon 😉