How to Implement "dark mode" with Gatsby & React Hooks
Kim Hart
Posted on August 1, 2019
I recently launched my new portfolio site and I have to say, I'm super proud! My old site was built on Wix years before I learned how to code and was in need of a major design update.
I landed on Gatsby for my setup and Netlify for my deployment platform and guys, I cannot recommend each of them enough. But this post isn't about that!
Why dark mode?
Turns out, a lot of people like dark-themed internet things (just ask Twitter). I chose to implement toggleable sunrise and sunset themes on my portfolio because it adds a level of interactivity to my otherwise static site, allowed me to play with more complex CSS, and it lets users customize their experience. It even persists through sessions via localStorage
!
What'd I use?
I considered building this myself until I found this tool called use-dark-mode. In short, it's a custom React Hook that handles the storage part for you. Their docs are pretty great, but I'll walk you through my use case as well.
Implementation
- You must use
react@16.8.0
or greater which includes Hooks - This only works in functional components, so if you're using older React class components with non-hook lifecycle methods, you may have a hard time.
1. Install
You'll install both use-dark-mode
and its Gatsby-specific plugin that helps with overall rendering of your themes:
yarn add use-dark-mode gatsby-plugin-use-dark-mode
2. Add to Gatsby config
To prevent a flash of default-styled content on page load, add this block to your gatsby-config.js
file. (More in the docs)
{
resolve: "gatsby-plugin-use-dark-mode",
options: {
classNameDark: "dark-mode",
classNameLight: "light-mode",
storageKey: "darkMode",
minify: true,
},
}
Note: you can name these classes whatever you like! I stuck with the defaults.
3. Add to React
Here's a ultra-simplified version of my hero component. It contains two icon components (sunrise and sunset) that fire handleTheme
on click, which launch either darkMode.enable()
or darkMode.disable()
depending on their props.
The goal here is to change to dark mode when you click sunset, and light mode when you click sunrise.
import React from "react"
import useDarkMode from "use-dark-mode"
import Sunrise from "../components/icons/sunrise"
import Sunset from "../components/icons/sunset"
const Hero = () => {
// Instantiate with the default behavior, in this case, it defaults to light-mode
// This places "light-mode" class on document.body, as outlined in my gatsby-config.js
const darkMode = useDarkMode(false);
// Custom function that handles the toggling
// When called, it replaces the class on document.body and holds it in localStorage
const handleTheme = theme => theme === "dark" ? darkMode.enable() : darkMode.disable();
return (
<div className="hero">
<Sunrise onClick={handleTheme} />
<Sunset onClick={handleTheme} />
</div>
)
}
export default Hero;
The sunset and sunrise icon components are super similar, they just pass different values ("light" and "dark"). Here's a slimmed-down version of Sunset:
import React from "react"
const Sunset = (props) => {
// If the `onClick` prop exists, call it with 'dark'
const handleClick = () => props.onClick && props.onClick('dark');
return (
<div className="theme-toggle" onClick={handleClick}>...</div>
)
}
- Note: you could also accomplish this with passing boolean values (i.e. "true" for dark, but I chose to keep it more readable and used strings)
CSS
Now that we have the class on document.body
toggling between light-mode and dark-mode when we click the sunrise or sunset icons, we can adjust our CSS to reflect the changes.
I used Less, which makes it super easy to apply rules based on parent values. Again, this is simplified, but hopefully you get the idea.
The .dark-mode &
selector will look for anytime the dark-mode
class exists on a higher component (in this case, the body
tag). You can then apply whatever rules you need — in this case it's a variable for the background colors.
.hero {
background: @sunrise-gradient;
.dark-mode & {
background: @sunset-gradient;
}
}
... and that's it!
Conclusion
You don't have to completely recreate the wheel to implement dark mode in a Gatsby app. Hopefully this was helpful and I'm happy to answer any questions in the comments!
Posted on August 1, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.