Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[material-ui][nextjs] useLocalStorage for theme name causes theme styles to get out of sync #41847

Closed
RickyRoller opened this issue Apr 10, 2024 · 1 comment
Assignees
Labels
customization: theme Centered around the theming features nextjs package: material-ui Specific to @mui/material

Comments

@RickyRoller
Copy link

RickyRoller commented Apr 10, 2024

Steps to reproduce

Link to live example: https://stackblitz.com/~/github.com/RickyRoller/mui-local-storage-bug

Steps:

  1. Switch between light and dark mode, everything works correctly
  2. Switch to light mode, reload the page, switching between themes is broken

Step 1
2024-04-10 14 31 33

Step 2
2024-04-10 14 31 52

It seems like the theme styles are getting out of sync with the styles from NextJS's pre-render of the app.
Looking at the DOM, there are pre-render styles that are always dominating the actual style tags in the app.

Steps to fix:

  • Add an extra useState in-between the local storage and the theme.
    • This is what I've gone with for now, but this was incredibly difficult to find so it would nice to know why it's broken in the first place
  • Don't use local storage

Current behavior

If you save the non-default theme in local storage, the theme styles will break upon reloading the app.

Expected behavior

Theme styles should always be in sync with the theme value passed to the component.

Context

I want to store the theme name in local storage so that the user's last selected preference is loaded by default

Your environment

npx @mui/envinfo
System:
    OS: macOS 14.1.1
  Binaries:
    Node: 20.10.0 - ~/.nvm/versions/node/v20.10.0/bin/node
    npm: 10.2.3 - ~/.nvm/versions/node/v20.10.0/bin/npm
    pnpm: Not Found
  Browsers:
    Chrome: 123.0.6312.107
    Edge: Not Found
    Safari: 17.1
  npmPackages:
    @emotion/react: latest => 11.11.4 
    @emotion/styled: latest => 11.11.5 
    @mui/base:  5.0.0-beta.40 
    @mui/core-downloads-tracker:  5.15.15 
    @mui/icons-material: latest => 5.15.15 
    @mui/material: latest => 5.15.15 
    @mui/material-nextjs: latest => 5.15.11 
    @mui/private-theming:  5.15.14 
    @mui/styled-engine:  5.15.14 
    @mui/system:  5.15.15 
    @mui/types:  7.2.14 
    @mui/utils:  5.15.14 
    @types/react: latest => 18.2.75 
    react: latest => 18.2.0 
    react-dom: latest => 18.2.0 
    typescript: latest => 5.4.5 

Search keywords: useLocalStorage, NextJS

@RickyRoller RickyRoller added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Apr 10, 2024
@danilo-leal danilo-leal changed the title NextJS: useLocalStorage for theme name causes theme styles to get out of sync [material-ui][nextjs] useLocalStorage for theme name causes theme styles to get out of sync Apr 11, 2024
@danilo-leal danilo-leal added package: material-ui Specific to @mui/material customization: theme Centered around the theming features nextjs labels Apr 11, 2024
@brijeshb42
Copy link
Contributor

brijeshb42 commented Apr 12, 2024

Here's a small change I made in your themeProvider

import { ThemeProvider as MuiThemeProvider } from '@mui/material/styles';
import {createContext, PropsWithChildren, useCallback, useEffect, useMemo, useState} from "react";
import theme from '../src/theme';

type IThemeSwitch = {
  setTheme: (name: string) => void;
}

export const SwitchContext = createContext<IThemeSwitch>({
  setTheme: () => undefined,
});

export const ThemeProvider = ({ children}: PropsWithChildren) => {
  const [themeName, setTheme] = useState('');

  const setThemeWithLs = useCallback((newTheme: string) => {
    setTheme(newTheme);
    window.localStorage.setItem('themeName', newTheme);
  }, []);
  const switchValue = useMemo(() => ({ setTheme: setThemeWithLs }), [setThemeWithLs])

  useEffect(() => {
    const lsTheme = window.localStorage.getItem('themeName');
    if (lsTheme) {
      setTheme(lsTheme);
    }
  }, [])

  const themeValue = useMemo(() => {
    return themeName === 'dark' ? theme.dark : theme.light;
  }, [themeName])

  return (
    <SwitchContext.Provider value={switchValue}>
      <MuiThemeProvider theme={themeValue}>
        {children}
      </MuiThemeProvider>
    </SwitchContext.Provider>
  )
}

There was a hydration mismatch because of how the use-local-storage library initialized that state.
Not that the fix might have a flash of theme change (BE sending light and then client setting it to dark). So you might need to use cookies for saving theme.

Feel free to re-open if you face any other issue.

@brijeshb42 brijeshb42 removed the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Apr 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
customization: theme Centered around the theming features nextjs package: material-ui Specific to @mui/material
Projects
None yet
Development

No branches or pull requests

3 participants