Creating Light/Dark mode on a React App with Context

--

Nowadays, Dark Mode is a must. At least for those who work all day in front of a computer screen, having Dark Mode enabled can be a blessing to the eyes. Pretty much all major websites offer Dark Mode, such as Twitter and GitHub. One Dark Pro theme for VSCode has 4,681,547 downloads, making it one of the most used themes for the beloved code editor.

With that in mind, I decided to try to apply what I’ve learned from the React course I took (by Colt Steele, available on Udemy) and created a small web app with light / dark mode.

You can see the final result here: lightswitch.

With React, setting a theme for your web app is way easier than, let’s say, if you are using only HTML / CSS and vanilla JavaScript. Of course, you can make up a script to do the job, but React tools such as useState and Context are a perfect fit for the job.

It is a simple way to get all your components on the same page, sharing the information they need to display the correct theme, avoiding the need to recreate the same content twice — one for each theme.

Here is how I did it — and I want to make it clear that I know this is not replicable to a big application, but it helped me understand how to set a nice foundation.

First, I created my app using npx create-react-app lightswitch .

After deleting pretty much everything from App.js and App.css, I created Container.js to… contain all my content. Duh.

My main goal here was to use Context. From React’s documentation:

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

With Context, I could create my Dark Mode Context and pass it through all my components, so they would all have access to the current theme mode selected by the user. The user’s choice would be stored on state, coming from Dark Mode Context.

I created a new folder called context and inside that folder, the DarkModeContext.js.

To use Context, you basically have to call createContext() to create the wanted context, and then set a context provider function to make it accessible to other components. To understand it further, check the documentation.

Here is my DarkModeContext:

But it doesn’t do anything, does it?

It doesn’t. What I need from this context is to know whether the user wants dark mode or not, and a way to manipulate it.

So basically I need to create a darkMode state that will evaluate to true or false — a boolean state —, and a toggle function for darkMode.

Using useState is quite simple, and I can pass the toggle function and the state down to all my components:

My DarkModeContext is pretty much done. You can see above that I am exporting DarkModeContext and DarkModeProvider. The reason is that I need to tell my components to use it.

With DarkModeProvider I pass data through. That’s why I have {props.children} . Back to App.js I imported DarkModeProvider and encapsulated my Container component with it. Now Container has access to DarkModeContext and its data (darkMode and toggleDarkMode). If I had more components where I needed DarkModeContext, I would simply encapsulate them with DarkModeProvider.

Now I can import DarkModeContext in my Container.js component. With useContext again I can extract the data I need from it:

To see if it works, I created two different classes — Container-dark and Container-light — and stylized them both as I wanted.

With access to the darkMode state I can dynamically apply the classes I want to my div. Obviously, on a larger scale that approach would be counterproductive, and there are libraries to organize styles inside objects, so you can manage dark/light preferences most effectively. For my purposes, controlling classes work just fine.

If darkMode evaluates to true, the app will display a dark background. If not, it will display a light background.

But I need a way to manipulate it. And it is pretty easy to do so. Since toggleDarkMode is coming from DarkModeContext, I can manipulate it inside any component encapsulated by DarkModeProvider.

To do so I created a new component called Lightswitch, which is, well, a light switch.

It consists of a simple light switch icon designed by Jeremy Loyd (public domain). I use it inside Container.

Following the same logic applied above to access darkMode on Container, I extracted darkMode and toggleDarkMode from DarkModeContext on the Lightswitch component. With them, I can display the on / off light switch, and change the theme mode.

How it looks:

Cute, right?

And that’s pretty much it. With Context I was able to share data through my components, and although I could achieve the same result simply passing props down (which would be easier, since this project is pretty small), Context offers a great solution when you have multiple components relying on the same data. If it changes, such change must be communicated to all components using it.

(On the final project there’s a cool neon-looking CSS style used on the text, which I took from CodePen and can be seen below, on GitHub)

I applied the same logic to my personal website — gschincariol.com, feel free to see how it works there by clicking on the lightbulb.

This project repository can be found here.

Hope you enjoyed this piece. Feel free to comment, just avoid being an asshole :)

You can find me on Twitter or GitHub.

My musical recommendation of the day: Mulholland Dr., by The Palms

--

--