[material-ui][next] AppCacheProvider with Next v13 and v14 Pages Router causes flickering
Steps to reproduce
I have a Nextjs app with Pages router, Typescript and Tailwind. I am following the Next integration guide
Here is my _app.tsx
import "@/styles/globals.css";
import Head from "next/head";
import type { AppProps } from "next/app";
import { AppCacheProvider } from '@mui/material-nextjs/v13-pagesRouter';
import { ThemeProvider, createTheme } from '@mui/material/styles';
import { Nunito } from 'next/font/google';
const nunito = Nunito({
weight: ['400', '500', '600', '700'],
subsets: ['latin', 'cyrillic'],
});
const theme = createTheme({
typography: {
fontFamily: 'Nunito, sans-serif',
},
});
export default function App({ Component, pageProps }: AppProps) {
return (
<AppCacheProvider>
<Head>
<link rel="icon" href="/tcfavicon.ico" />
</Head>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</AppCacheProvider>
)
};
and _document.tsx:
import { Html, Head, Main, NextScript, DocumentContext, DocumentProps } from "next/document";
import {
DocumentHeadTags,
documentGetInitialProps,
DocumentHeadTagsProps,
} from '@mui/material-nextjs/v13-pagesRouter';
export default function Document(props: DocumentProps & DocumentHeadTagsProps) {
return (
<Html lang="en">
<Head>
<DocumentHeadTags {...props} />
<meta name="emotion-insertion-point" content="" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
Document.getInitialProps = async (ctx: DocumentContext) => {
const finalProps = await documentGetInitialProps(ctx);
return finalProps;
};
Current behavior
Whenever a page loads/reloads you can see the button component flickering.
https://github.com/mui/material-ui/assets/52756776/67328e37-72d1-4ca0-b882-1cd4f64b946a
I have also tested with Next v14 with same result. When I remove the AppCacheProvider from _app.tsx I don't get that problem.
Expected behavior
No response
Context
No response
Your environment
npx @mui/envinfo
System:
OS: Windows 10 10.0.19045
Node: 21.7.1 - C:\Program Files\nodejs\node.EXE
npm: 10.5.0 - C:\Program Files\nodejs\npm.CMD
pnpm: Not Found
Browsers:
Chrome: 122.0.6261.129
Edge: Chromium (122.0.2365.92)
npmPackages:
@emotion/react: ^11.11.4 => 11.11.4
@emotion/styled: ^11.11.0 => 11.11.0
@mui/base: 5.0.0-beta.40
@mui/core-downloads-tracker: 5.15.14
@mui/material: ^5.15.14 => 5.15.14
@mui/material-nextjs: ^5.15.11 => 5.15.11
@mui/private-theming: 5.15.14
@mui/styled-engine: 5.15.14
@mui/system: 5.15.14
@mui/types: 7.2.14
@mui/utils: 5.15.14
@types/react: latest => 18.2.69
react: latest => 18.2.0
react-dom: latest => 18.2.0
typescript: latest => 5.4.3
Search keywords: AppCacheProvider
I have the same behaviour, I have no idea what we are missing but clearly the css injection is not working on the server side.
You can clearly observe it by simply disabling javascript (client side), with the following code replacing <Component {...pageProps} />:
<ThemeProvider theme={theme}>
<Button variant="contained">Test button</Button>
</ThemeProvider>
This buttons outputs:
<button class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium MuiButton-colorPrimary mui-d00fc6-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button">Test button</button>
But if you look for any of these class names in the head of your page (while JS is disabled) you'll notice that none is present.
On the other hand, if we enable JS, we will have the CSS injected, something like this:
<head>
<style data-emotion="mui" data-s="">.mui-f16x46-MuiButton-root{font-family:Nunito,sans-serif;}</style>
</head>
That means CSS is injected on the client side, which is why the SSR rendered page is not correct (which causes the flickering). I haven't found the solution to this yet. It's strange because the install steps are pretty simple so I'm not sure what I'm missing.
Maybe that's a version issue.
My environment
npx @mui/envinfo
System:
OS: macOS 13.6.1
Binaries:
Node: 18.19.0 - ~/.nvm/versions/node/v18.19.0/bin/node
npm: 10.2.3 - ~/.nvm/versions/node/v18.19.0/bin/npm
pnpm: 8.6.6 - /opt/homebrew/bin/pnpm
Browsers:
Chrome: 124.0.6367.78
Edge: Not Found
Safari: 17.1
npmPackages:
@emotion/react: ^11.9.3 => 11.9.3
@emotion/styled: ^11.9.3 => 11.9.3
@mui/base: 5.0.0-beta.40
@mui/core-downloads-tracker: 5.15.15
@mui/icons-material: ^5.8.4 => 5.8.4
@mui/material: ^5.15.15 => 5.15.15
@mui/material-nextjs: ^5.15.11 => 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: ^18.0.9 => 18.0.15
react: ^18.2.0 => 18.2.0
react-dom: ^18.2.0 => 18.2.0
typescript: ^4.6.4 => 4.7.4
@fbozhkov You miss this line:
- export default function App({ Component, pageProps }: AppProps) {
+ export default function App({ Component, pageProps, …props }: AppProps) {
return (
- <AppCacheProvider>
+ <AppCacheProvider {…props}>
<Head>
<link rel="icon" href="/tcfavicon.ico" />
</Head>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</AppCacheProvider>
)
};
I think we could improve the docs by:
- Then, inside pages/_app.tsx, import the AppCacheProvider component and render it as the root element:
+ Then, inside pages/_app.tsx, import the AppCacheProvider component and render it as the root element (make sure to **pass the props** from `App` to the provider):
I am closing this. Tested by following the docs and it still work.