material-ui icon indicating copy to clipboard operation
material-ui copied to clipboard

Material UI 6 CssVarsProvider causes Suspense to reload async components

Open awinogrodzki opened this issue 1 year ago • 5 comments

Steps to reproduce

Link to live example: https://next-15-mui-6-suspense-flicker.vercel.app/

Steps:

  1. Open the link
  2. Refresh the page
  3. See Suspense boundary to re-render async loaded component

https://github.com/user-attachments/assets/ef7ba60e-3888-4010-b0b0-527c2a3567d1

This behaviour is a regression – using Material 5.16.7 causes Suspense to work as expected:

https://github.com/user-attachments/assets/0aea80ab-b7eb-4893-90cc-6cd12fda7fc0

Link to repo with Material 6 reproduction: https://github.com/awinogrodzki/next-15-mui-6-suspense-flicker

Link to branch with working Material 5 example: https://github.com/awinogrodzki/next-15-mui-6-suspense-flicker/tree/material-5

Current behavior

On first render, async components inside Suspense boundary are reloaded and re-rendered

Expected behavior

Should work like in Material 5 –  async component tree should not be re-mounted on first render

Context

I am working on a https://ensite.in, which is a no-code website builder. Due to large amount of building-blocks user can choose from, the whole app renders as an async-component tree

Your environment

npx @mui/envinfo
  System:
    OS: macOS 14.2.1
  Binaries:
    Node: 20.12.0 - ~/.nvm/versions/node/v20.12.0/bin/node
    npm: 10.5.0 - ~/.nvm/versions/node/v20.12.0/bin/npm
    pnpm: Not Found
  Browsers:
    Chrome: 127.0.6533.100
    Edge: Not Found
    Safari: 17.2.1
  npmPackages:
    @emotion/react: ^11.13.0 => 11.13.0 
    @emotion/styled: ^11.13.0 => 11.13.0 
    @mui/core-downloads-tracker:  6.0.0-dev.240424162023-9968b4889d 
    @mui/material: ^6.0.0-beta.5 => 6.0.0-beta.5 
    @mui/material-nextjs: ^6.0.0-beta.4 => 6.0.0-beta.4 
    @mui/private-theming:  6.0.0-beta.5 
    @mui/styled-engine:  6.0.0-beta.5 
    @mui/system:  6.0.0-beta.5 
    @mui/types:  7.2.15 
    @mui/utils:  6.0.0-beta.5 
    @types/react: ^18 => 18.3.3 
    react: 19.0.0-rc-f994737d14-20240522 => 19.0.0-rc-f994737d14-20240522 
    react-dom: 19.0.0-rc-f994737d14-20240522 => 19.0.0-rc-f994737d14-20240522 
    typescript: ^5 => 5.5.4

Search keywords: material 6 suspense css vars cssvarsprovider

awinogrodzki avatar Aug 11 '24 11:08 awinogrodzki

@awinogrodzki in v6, the CssVarsProvider contains a rerender (not a remount) after hydration so that the mode is updated on the client. It's strange that a rerender will cause a Suspense to display a fallback. Is this the expected behavior?

siriwatknp avatar Aug 14 '24 02:08 siriwatknp

Good point @siriwatknp. I think that the remount comes from a root async component after mode re-render in CssVarsProvider. Let me run some more experiments to be sure and I'll get back to you

awinogrodzki avatar Aug 14 '24 17:08 awinogrodzki

@siriwatknp, after running different experiments I can confirm that no remount happens. I think I must've observed it when isolated the case in the real app. I don't think it's related to this issue however, so let's just ignore it.

I still think the issue lies somewhere inside CssVarsProvider. Here's some experiments that I've performed:

Recordings of the production build

Material UI 6

Code for layout: https://github.com/awinogrodzki/next-15-mui-6-suspense-flicker/blob/main/app/layout.tsx

You can notice that it's basic layout created using create-next-app updated with InitColorSchemeScript, CssVarsProvider and AppRouterCacheProvider, according to the docs

Code for theme: https://github.com/awinogrodzki/next-15-mui-6-suspense-flicker/blob/main/app/theme.ts

It's just a basic theme with custom selector

Light mode

https://github.com/user-attachments/assets/71410df2-f3e1-453d-820a-5142ab2850e2

Dark mode

https://github.com/user-attachments/assets/e0e5877e-923b-4d8f-933a-6ea352a680a8

Material UI 5

Code for layout: https://github.com/awinogrodzki/next-15-mui-6-suspense-flicker/blob/material-5/app/layout.tsx Code for theme: https://github.com/awinogrodzki/next-15-mui-6-suspense-flicker/blob/material-5/app/theme.ts

Light mode

https://github.com/user-attachments/assets/420a7832-ab08-4d9f-ac45-cd6b65946a1b

Dark mode

https://github.com/user-attachments/assets/c10a0561-f349-4057-88de-7f07fecd66b6

Control

I also tested the behavior with MUI6, but without CssVarProvider in layout.tsx:

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body className={`${geistSans.variable} ${geistMono.variable}`}>
        <AppRouterCacheProvider>
          <InitColorSchemeScript  attribute="[data-scheme-%s]" />
          {children}
        </AppRouterCacheProvider>
      </body>
    </html>
  );
}

It also does work as expected, which points that the problem is somewhere inside CssVarsProvider

Light mode

https://github.com/user-attachments/assets/c9174e33-a86e-4195-8b93-1530ab2d4744

Dark mode

https://github.com/user-attachments/assets/46910d50-441e-4a58-a0ef-e2c2a929445c

awinogrodzki avatar Aug 14 '24 18:08 awinogrodzki

Hey @siriwatknp,

Are there any updates on the issue?

I have updated Material UI to the latest version – 6.0.2, and the issue still exists.

I have also replaced CssVarsProvider and extendTheme with recommended ThemeProvider + createTheme – the issue is still reproducible

Is there anything I could do to help?

awinogrodzki avatar Sep 06 '24 13:09 awinogrodzki

I've cloned material-ui repository to debug the issue and it seems that everything starts working properly when I comment this line:

https://github.com/mui/material-ui/blob/master/packages/mui-system/src/cssVars/useCurrentColorScheme.ts#L163

Is this state update necessary?

With this line commented system, light and dark mode seems to be working correctly and Suspense also works as expected.

Maybe you could at least provide a opt-out from this behaviour?

awinogrodzki avatar Sep 06 '24 14:09 awinogrodzki

Maybe you could at least provide a opt-out from this behaviour?

That'd be possible.

siriwatknp avatar Nov 13 '24 10:11 siriwatknp

@awinogrodzki I created a PR to add disableClientRerender to ThemeProvider. Tested with your repo, it works. Please try:

"@mui/material": "https://pkg.csb.dev/mui/material-ui/commit/2b33a223/@mui/material",

siriwatknp avatar Nov 18 '24 08:11 siriwatknp

@siriwatknp awesome! I just tested and it works as expected. Thank you!

awinogrodzki avatar Nov 19 '24 09:11 awinogrodzki

@siriwatknp maybe it's also related https://github.com/mui/material-ui/issues/43263

I just tried with disableClientRerender and "@mui/material": "https://pkg.csb.dev/mui/material-ui/commit/2b33a223/@mui/material",

And it works as expected. No more flickering

mtr1990 avatar Nov 19 '24 20:11 mtr1990

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] @awinogrodzki 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.

github-actions[bot] avatar Nov 26 '24 03:11 github-actions[bot]