Mark
Mark

Reputation: 783

Next.js Material UI (MUI) how to set Dark Mode with local storage

I am trying to get dark mode working in a Next.js app with MUI. The problem is that I cannot seem to get dark mode persistent once it has been set. I am setting the state with the help of createContext since the toggle button is inside a component.

When toggling it sets the state to either true or false and sets it in the localStorage. However, on page refresh it goes back to a false state.

I have tried many solutions but cannot seem to get it to work.

At the moment I am doing the following within _app.js (this is merely a snippet, full code in link):

  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    const mode = localStorage.getItem("mode");
    // set mode
    setDarkMode(mode);
  }, []);

  useEffect(() => {
    localStorage.setItem("mode", darkMode);
  }, [darkMode]);

  // Set dark mode based on media query
  const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
  useEffect(() => {
    setDarkMode(prefersDarkMode);
  }, [prefersDarkMode]);

  <ColorModeContext.Provider value={{ darkMode, setDarkMode }}>
    <ThemeProvider theme={darkMode ? darkTheme : theme}>
      {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
      <CssBaseline />
      <Component {...pageProps} />
    </ThemeProvider>
  </ColorModeContext.Provider>

I have setup a CodeSandbox with the full code:

https://codesandbox.io/s/next-js-mui-dark-mode-n6l24?file=/pages/_app.js

Any help here would be appreciated!

Upvotes: 0

Views: 4639

Answers (3)

mjw
mjw

Reputation: 21

There is an error with your csb, does not load properly. In case you want to persist dark mode and without flashing problem on MUI. Must use next-themes, easy to set up and I did follow this method https://github.com/DiMatteoL/buzzrank-tutorial and live preview https://buzzrank-tutorial.vercel.app/.

Upvotes: 0

gdlmx
gdlmx

Reputation: 6789

The third effect in your code setDarkMode(prefersDarkMode) will overwrite the darkMode in all cases, making any value from the local storage ineffective.

Moreover, it is better to define a custom setDarkMode function and pass it to the ColorModeContext provider.

  // Set dark mode based on media query
  const prefersDarkMode = useMediaQuery("(prefers-color-scheme: dark)");
  
  // use the media query setting as the default (which can be overwritten)
  const [darkMode, setDarkMode] = useState(prefersDarkMode);

  useEffect(() => {
    const mode = localStorage.getItem("mode");
    // set mode
    console.log(`get localStore ${mode}`);
    if(mode !== null){
        setDarkMode(mode  === "true");
    }
  }, []);

  // set DarkMode triggered by user
  const _setDarkMode = (newmode) => {
    console.log(`set localStore ${newmode}`);
    localStorage.setItem("mode", newmode);
    setDarkMode(newmode);
  };

  return (
    ... 
    <ColorModeContext.Provider
        value={{ darkMode, setDarkMode: _setDarkMode }}
    >
        ...
    </ColorModeContext.Provider>
    ...
  );

https://codesandbox.io/s/next-js-mui-dark-mode-forked-ucpvo?file=/pages/_app.js

Upvotes: 1

hotcakedev
hotcakedev

Reputation: 2504

As I mentioned, darkMode should not be just false, try to get the darkModefrom local storage first and then use it as a default value.

Here's one of my repo. https://github.com/hotcakedev628/mail-app-demo/blob/master/src/contexts/SettingsContext.js

Upvotes: 0

Related Questions