I recently tackled an annoying flicker when using dark mode on my website. The page would briefly flash in light mode before switching over.
This describes the Gatsby 5 and React 18 setup I was using in December 2024.
The cause
The dark mode class was being added by a script inside the page body. By the time the script ran, the browser had already started rendering the page.
The fix
To fix it, I moved the script into the document <head>. This makes it run
before any content is rendered. Blocking the page is usually something to
avoid, but the script is small and it prevents the flicker.
I created a wrapper for the page <head> that automatically appends the setup
script:
const setupDarkModeScript = `
(() => {
const darkMode = localStorage.getItem("website.darkMode");
const prefersDarkMode = window.matchMedia("(prefers-color-scheme: dark)").matches;
if (darkMode === "true" || (darkMode === null && prefersDarkMode)) {
document.documentElement.classList.add("dark-mode");
}
})();
`.trim();
export const PageHead: React.FC<React.PropsWithChildren> = ({ children }) => (
<>
{children}
<script>{setupDarkModeScript}</script> </>
);Having the script as a string also keeps me from running into browser API issues during server-side rendering.
The flicker is gone, and dark mode now loads smoothly.