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

[MUI v6] Setting default mode flickers the light/dark theme at first render #43972

Closed
MartinXPN opened this issue Oct 3, 2024 · 6 comments · Fixed by #44139
Closed

[MUI v6] Setting default mode flickers the light/dark theme at first render #43972

MartinXPN opened this issue Oct 3, 2024 · 6 comments · Fixed by #44139
Assignees
Labels
bug 🐛 Something doesn't work customization: theme Centered around the theming features package: system Specific to @mui/system

Comments

@MartinXPN
Copy link

MartinXPN commented Oct 3, 2024

Steps to reproduce

Link to the initial issue: #43622

Steps:

  1. Set your system (OS) default theme to dark
  2. Set the default MUI theme mode to light
  3. Open the page in incognito mode
  4. The page first renders with the dark mode and then changes to light, causing flickering

Make sure to re-open the page in incognito mode or change the modeStorageKey. Otherwise, the key will be stored and you won't notice any flickering.

Here is a deployed example: https://profound.academy

Current behavior

If your system default mode is dark, and you set your website default theme to be light, the very first render is dark, which then switches to light, causing flickering. That's pretty unpleasant for the visitors.

Expected behavior

It should render using the defaultMode right away without any flickering.

Context

No response

Your environment

npx @mui/envinfo

Using Chrome

  System:
    OS: macOS 14.6.1
  Binaries:
    Node: 20.10.0 - /opt/homebrew/opt/node@20/bin/node
    npm: 10.2.3 - /opt/homebrew/opt/node@20/bin/npm
    pnpm: Not Found
  Browsers:
    Chrome: 129.0.6668.71
    Edge: Not Found
    Safari: 17.6
  npmPackages:
    @emotion/react: ^11.13.3 => 11.13.3 
    @emotion/styled: ^11.13.0 => 11.13.0 
    @mui/core-downloads-tracker:  6.1.2 
    @mui/icons-material: ^6.1.2 => 6.1.2 
    @mui/material: ^6.1.2 => 6.1.2 
    @mui/private-theming:  6.1.2 
    @mui/styled-engine:  6.1.2 
    @mui/system:  6.1.2 
    @mui/types:  7.2.17 
    @mui/utils:  5.16.6 
    @mui/x-charts: ^7.18.0 => 7.18.0 
    @mui/x-charts-vendor:  7.18.0 
    @mui/x-data-grid: ^7.18.0 => 7.18.0 
    @mui/x-date-pickers: ^7.18.0 => 7.18.0 
    @mui/x-internals:  7.18.0 
    @mui/x-tree-view: ^7.18.0 => 7.18.0 
    @types/react: ^18.3.10 => 18.3.10 
    react: ^18.3.1 => 18.3.1 
    react-dom: ^18.3.1 => 18.3.1 
    styled-components:  5.3.11 
    typescript: ^5.6.2 => 5.6.2 

Search keywords: default mode, flickering, MUI v6

@MartinXPN MartinXPN added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Oct 3, 2024
@zannager zannager added the package: system Specific to @mui/system label Oct 3, 2024
@DiegoAndai DiegoAndai moved this to Backlog in Material UI Oct 14, 2024
@DiegoAndai DiegoAndai removed the status in Material UI Oct 14, 2024
@DiegoAndai DiegoAndai moved this to Selected in Material UI Oct 14, 2024
@MartinXPN
Copy link
Author

@aarongarciah do you know why this might be happening?

@siriwatknp
Copy link
Member

Can you provide a code sample that you are using for the ThemeProvider? I tested locally with a fresh Next.js project and it works fine.

// theme.js
const theme = createTheme({
  cssVariables: {
    colorSchemeSelector: "class",
  },
  colorSchemes: {
    light: true,
    dark: true,
  },
  typography: {
    fontFamily: roboto.style.fontFamily,
  },
});

// layout.tsx
export default function RootLayout(props: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AppRouterCacheProvider options={{ enableCssLayer: true }}>
          <ThemeProvider defaultMode="light" theme={theme}>
            {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
            <CssBaseline />
            {props.children}
          </ThemeProvider>
        </AppRouterCacheProvider>
      </body>
    </html>
  );
}

@siriwatknp siriwatknp added status: waiting for author Issue with insufficient information customization: theme Centered around the theming features and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Oct 17, 2024
@MartinXPN
Copy link
Author

MartinXPN commented Oct 17, 2024

Sure, I think you're missing the InitColorSchemeScript. If I remove it the flickering goes away on the first render, but the whole app flickers on all the next renders if I switch to dark mode and refresh the page.

Here is the ThemeProvider.tsx:

'use client';

import {memo, ReactNode} from "react";
import {Roboto} from "next/font/google";
import {createTheme, ThemeProvider as MuiThemeProvider} from "@mui/material/styles";
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
import CssBaseline from "@mui/material/CssBaseline";
import ColorModeContextProvider from "@/theme/ColorModeContext";
import {AppRouterCacheProvider} from "@mui/material-nextjs/v14-appRouter";


const font = Roboto({weight: ['300', '400', '500', '700'], subsets: ['latin']});

export const theme = createTheme({
    cssVariables: {colorSchemeSelector: 'class'},
    defaultColorScheme: 'light',
    typography: {
        fontFamily: font.style.fontFamily,
        h1: {fontSize: 42, fontWeight: 'bold'},
        h2: {fontSize: 30},
        h3: {fontSize: 26},
        h4: {fontSize: 20},
        h5: {fontSize: 18},
        h6: {fontSize: 16},
    },
    colorSchemes: {
        light: {
            palette: {
                background: {default: '#ffffff', paper: '#ffffff'},
                primary: {main: '#212b36'},
                secondary: {main: '#fa541c'},
                info: {main: '#f44336'},
                success: {main: '#4caf50'},
                error: {main: '#f44336'},

                // @ts-ignore
                console: {main: '#e0e0e0'},
                secondaryAction: {main: '#000000'},
                neutral: {main: '#ffffff', contrastText: '#212121'},
                unavailable: {main: '#363636'},
            },
        },
        dark: {
            palette: {
                background: {default: '#171717', paper: '#212121'},
                primary: {main: '#ffffff'},
                secondary: {main: '#fa541c'},
                info: {main: '#f44336'},
                success: {main: '#388e3c'},
                error: {main: '#d32f2f'},

                // @ts-ignore
                console: {main: '#212121'},
                secondaryAction: {main: '#fff'},
                neutral: {main: '#212121', contrastText: '#fff'},
                unavailable: {main: '#797979'},
            },
        },
    },
});


function ThemeProvider({children}: {children: ReactNode}) {
    return <>
        <AppRouterCacheProvider options={{ enableCssLayer: true }}>
        <InitColorSchemeScript modeStorageKey="theme-mode" attribute="class" />
        <MuiThemeProvider modeStorageKey="theme-mode" defaultMode="light" theme={theme}>
        <ColorModeContextProvider>
            <CssBaseline />
            {children}
        </ColorModeContextProvider>
        </MuiThemeProvider>
        </AppRouterCacheProvider>
    </>
}

export default memo(ThemeProvider);

And here is the ColorModeContext.tsx:

import {createContext, memo, ReactNode} from "react";
import {useColorScheme} from "@mui/material/styles";

export type PaletteMode = 'light' | 'dark' | 'system';

interface ColorModeContextProps {
    mode: PaletteMode;
    setMode: (mode: PaletteMode) => void;
}

export const ColorModeContext = createContext<ColorModeContextProps>({
    mode: 'light',
    setMode: (_: PaletteMode) => {},
});

function ColorModeContextProvider({children}: {children: ReactNode}) {
    const {mode, setMode} = useColorScheme();

    return <ColorModeContext.Provider value={{mode: mode ?? 'light', setMode: setMode}}>
        {children}
    </ColorModeContext.Provider>
}

export default memo(ColorModeContextProvider);

When I open the app in incognito mode (fresh window every time), it opens up in dark mode and stays in dark mode for 1-2 seconds. After which, it switches to light mode.

@github-actions github-actions bot added status: waiting for maintainer These issues haven't been looked at yet by a maintainer and removed status: waiting for author Issue with insufficient information labels Oct 17, 2024
@siriwatknp siriwatknp added bug 🐛 Something doesn't work and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Oct 17, 2024
@siriwatknp
Copy link
Member

Sure, I think you're missing the InitColorSchemeScript. If I remove it the flickering goes away on the first render, but the whole app flickers on all the next renders if I switch to dark mode and refresh the page.

Gotcha, you are right. The problem is the InitColorSchemeScript. At the beginning, the default mode is system (no local storage yet).

I will open a PR to fix this. You will need to provide the defaultMode to InitColorSchemeScript in the future too.

@MartinXPN
Copy link
Author

MartinXPN commented Oct 17, 2024

Sounds good, I think it would be pretty straightforward from the users' perspective (I assume that in most projects ThemeProvider and InitColorSchemeScript are located in the same file, so keeping them in sync shouldn't be a problem).

Copy link

This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue.
Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.

Note

@MartinXPN How did we do? Your experience with our support team matters to us. If you have a moment, please share your thoughts in this short Support Satisfaction survey.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work customization: theme Centered around the theming features package: system Specific to @mui/system
Projects
Status: Done
Development

Successfully merging a pull request may close this issue.

4 participants