This blog post is a follow up to Switching off the lights - Adding dark mode to your React app that I wrote a year ago. I finally took the time to fix my implementation which caused a lot of issues on server rendered websites and I wanted to share my solution with you.

An ugly hack

When I first added dark mode on my Gatsby projects, I encountered what you might know as the "Dark mode flashing" issue. The colors of the light mode would show up for a brief moment when refreshing a webpage.

Why does this issue show up? @JoshWComeau explains the reason behind this issue pretty well on his blog post CSS Variables for React Devs:

"Dark Mode" is surprisingly tricky, especially in a server-rendered context (like with Gatsby or Next.js). The problem is that the HTML is generated long before it reaches the user's device, so there's no way to know which color theme the user prefers.

To avoid this issue back when implementing it for the first time I did what I'd call an "ugly hack". I'd avoid rendering the whole website until the theme to render was known, and in the meantime, I'd just render a simple <div/> :

Code snippet from my first dark mode article featuring the ugly hack to avoid "dark mode flash" js Copy 1 if ( ! themeState . hasThemeLoaded ) { 2 3 4 5 6 7 8 return < div / > ; 9 } 10 const theme = themeState . dark ? theme ( 'dark' ) : theme ( 'light' ) ;

This ugly hack caused me some of the most frustrating problems I've had in a while, one of them even took me several days to figure out:

The core of the issue: I was rendering a <div/> when loading the website and reading the localStorage to set the proper theme (since it's async). This stopped gatsby from going further during the SSR build step and hence not generating the pages (with meta tags) of my blog — Maxime 🇫🇷 (@MaximeHeckel) August 20, 2019

(Again thank you @chrisbiscardi for taking the time to help me debug this)

I then brought another solution to this problem: add a display: hidden CSS style to the main wrapper until the theme was loaded as featured in this blog post. It fixed my SEO issues, but I was still not satisfied with this fix.

After reading Josh Comeau's blog post on using CSS variables along with Emotion Styled Components, I decided to leverage these to fix the dark mode flashing issue once and for all (no hack this time!).