Search code examples
reactjsdarkmode

Grab theme from local storage before page is rendered in react


I am building a small web application with react and have also included a ThemeSwitcher component. This component contains the visual aspects, i.e. the toggle, as well as a script to switch the theme and a script to access local storage. The ThemeSwitcher component then gets imported into the Header component, which in turn gets imported into index.js

When toggling from the dark-theme (default) to the light-theme, this information is saved to local storage:

let currentTheme = "dark-theme";

function switchTheme() {
        const body = document.querySelector("body");
        const transitionChecker = document.querySelector("#theme-selector");
    
        body.classList.add("transition");
        body.classList.toggle("light-theme");
        transitionChecker.addEventListener("transitionend", () => {
            body.classList.remove("transition");
        });
        currentTheme === "dark-theme"
            ? (currentTheme = "light-theme")
            : (currentTheme = "dark-theme");
    
        localStorage.setItem("storedTheme", currentTheme);
    }

I am accessing this value on page load using:

window.addEventListener("load", function () {
    let storedTheme = localStorage.getItem("storedTheme");
    if (storedTheme === "light-theme") switchTheme();
});

This works more or less as intended. However, on page load the dark-theme is shown for a split second, before switching over to the light-theme.

Is there any way to grab the intended theme before the page is rendered in order to avoid the default theme from flashing through?


Solution

  • So after some playing around, I believe to have a solution that works for me. I basically just put this right at the beginning of my App.js (which gets rendered in index.js) just before declaring the actual component:

    //get stored theme right at the beginning
    let currentTheme = localStorage.getItem("storedTheme");
    if (!currentTheme) currentTheme = "dark-theme"; // if no theme was stored, set to default --> dark-theme
    
    document.querySelector("body").classList.add(currentTheme);
    

    How I arrived at this solution:

    • I got started using create-react-app, so switching over to Next.js wasn't an option
    • Tried some things with setTimeOut, but couldn't get it to work
    • Tried using useEffect(), but this still let the dark theme flash through if the light-theme chosen previously
    • Moving the code outside of the actual App component, but still locating it within App.js worked.

    switchTheme() was also moved to App.js and is located at the very end.

    Not sure if this is a proper solution or if it just gives the browser this extra split second to grab the stored theme before it actually gets rendered. But it worked for me.