First of all, thank you all very much for the amazing material-ui.
I started my project from https://github.com/mui-org/material-ui/tree/master/examples/nextjs
Lately, I started to notice that my page after hydration looked weird - some styles were missing and some other times they were not applied to the expected elements.
Using React.StrictMode it tells me the following error:
Warning: Prop className
did not match. Server: "MuiSvgIcon-root makeStyles-lightBulb-47" Client: "MuiSvgIcon-root makeStyles-lightBulb-48"
Looks like the classNames generated at server rendering are different to the ones generated at browser rendering time.
Server-side rendering and client-side rendering should generate the same class names.
Steps (Code Sandbox):
className
did not match. Server: "MuiSvgIcon-root makeStyles-lightBulb-47" Client: "MuiSvgIcon-root makeStyles-lightBulb-48"Steps (Running locally)
className
did not match. Server: "MuiSvgIcon-root makeStyles-lightBulb-47" Client: "MuiSvgIcon-root makeStyles-lightBulb-48"The reproduction of the error will look something like:
The only difference between my repository (example) and material-ui/examples/nextjs is the React.StrictMode instead of React.Fragment inside _app.js
| Tech | Version |
| ----------- | ------- |
| Material-UI | latest (4.51) |
| React | latest (16.11.0) |
| Browser | Any browser |
| Node | 12 |
| Nextjs | latest (9.1.1)
PS - I saw that in the past some issues were created for something similar to this, but I couldn't apply the solutions on those issues to this specific issue. Most of those issues were using styled-components and none was using the material-ui example.
@oliviertassinari I think this is a bug, not a question.
If you see my reproduction steps, the only thing I did was to add StrictMode to material-ui/example/nextjs - no other code is changed
thank you for reporting it. I'm experiencing the same issue here..
I was able to solve it by going back to this versions:
"@material-ui/core": "3.9.3",
"@material-ui/styles": "3.0.0-alpha.10",
"next": "^9.1.1", // the latest version
_document.js:
/* eslint-disable prefer-destructuring */
import React from 'react'
import PropTypes from 'prop-types'
import Document, { Head, Main, NextScript } from 'next/document'
import flush from 'styled-jsx/server'
class MyDocument extends Document {
render() {
const { pageContext } = this.props
return (
<html lang="en" dir="ltr">
<Head>
<meta charSet="utf-8" />
{/* Use minimum-scale=1 to enable GPU rasterization */}
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
/>
{/* PWA primary color */}
<meta
name="theme-color"
content={
pageContext ? pageContext.theme.palette.primary.main : null
}
/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500"
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
MyDocument.getInitialProps = ctx => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
// Render app and page and get the context of the page with collected side effects.
let pageContext
const page = ctx.renderPage(Component => {
const WrappedComponent = props => {
pageContext = props.pageContext
return <Component {...props} />
}
WrappedComponent.propTypes = {
pageContext: PropTypes.shape().isRequired,
}
return WrappedComponent
})
let css
// It might be undefined, e.g. after an error.
if (pageContext) {
css = pageContext.sheetsRegistry.toString()
}
return {
...page,
pageContext,
// Styles fragment is rendered after the app and page rendering finish.
styles: (
<React.Fragment>
<style
id="jss-server-side"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: css }}
/>
{flush() || null}
</React.Fragment>
),
}
}
export default MyDocument
_app.js:
import React from 'react'
import App from 'next/app'
import getPageContext from '../lib/getPageContext'
import StylingProvider from '../lib/StylingProvider'
export default class MyApp extends App {
constructor() {
super()
this.pageContext = getPageContext()
}
componentDidMount() {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side')
console.log(jssStyles)
if (jssStyles) {
jssStyles.parentNode.removeChild(jssStyles)
}
}
render() {
const { Component, pageProps } = this.props
return (
<StylingProvider pageContext={this.pageContext}>
<Component pageContext={this.pageContext} {...pageProps} />
</StylingProvider>
)
}
}
StylingProvider.js:
import React from 'react'
import PropTypes from 'prop-types'
import { ThemeProvider, StylesProvider } from '@material-ui/styles'
import { MuiThemeProvider } from '@material-ui/core/styles'
import JssProvider from 'react-jss/lib/JssProvider'
import CssBaseline from '@material-ui/core/CssBaseline/CssBaseline'
const propTypes = {
pageContext: PropTypes.shape().isRequired,
children: PropTypes.node.isRequired,
}
function StylingProvider({ children, pageContext }) {
return (
<div>
<JssProvider
registry={pageContext.sheetsRegistry}
generateClassName={pageContext.generateClassName}
>
<StylesProvider
generateClassName={pageContext.generateClassName}
sheetsRegistry={pageContext.sheetsRegistry}
sheetsManager={pageContext.sheetsManager}
>
<MuiThemeProvider
theme={pageContext.theme}
sheetsManager={pageContext.sheetsManager}
>
<ThemeProvider theme={pageContext.theme}>
<CssBaseline />
{children}
</ThemeProvider>
</MuiThemeProvider>
</StylesProvider>
</JssProvider>
</div>
)
}
StylingProvider.propTypes = propTypes
export default StylingProvider
getPageContext.js :
import { SheetsRegistry } from 'jss'
import { createGenerateClassName } from '@material-ui/styles'
import theme from '../theme'
function createPageContext() {
return {
theme,
// This is needed in order to deduplicate the injection of CSS in the page.
sheetsManager: new Map(),
// This is needed in order to inject the critical CSS.
sheetsRegistry: new SheetsRegistry(),
// The standard class name generator.
generateClassName: createGenerateClassName(),
}
}
let pageContext
export default function getPageContext() {
// Make sure to create a new context for every server-side request so that data
// isn't shared between connections (which would be bad).
if (!process.browser) {
return createPageContext()
}
// Reuse context on the client-side.
if (!pageContext) {
pageContext = createPageContext()
}
return pageContext
}
I hope this will help someone straggling with MaterialUI and Next.js ClassName issue in the future
@amiral-jion it's good to know that it's a bug introduced in one of the new versions :)
I think this will take bit more time to fix since /styles
isn't StrictMode compatible.
We need to fix the following tests first:
https://github.com/mui-org/material-ui/blob/273bff4c652877e0b66fbfde00f860d734c37b59/packages/material-ui-styles/src/withStyles/withStyles.test.js#L21
https://github.com/mui-org/material-ui/blob/dc740b6346676ac57256e7eadf9b4d73e9d7a926/packages/material-ui-styles/src/styled/styled.test.js#L17
https://github.com/mui-org/material-ui/blob/947c9caf7a11beb0820465d37c898bae0c62c6b8/packages/material-ui-styles/src/makeStyles/makeStyles.test.js#L42
Switch those to strict: true
and then fix the failures.
Could it be a recent regression linked to a dependency/transitive dependency?
After more investigation, the problem seems to be the following: React randomly? triggers two renders before the hydration. The makeStyles()
logic updates the dynamic (not the static ones) style rules at each render, this increments the class names counter twice instead of once => mismatch. @eps1lon is likely on the right path.
For me it was the automatic imports of my IDE after copy pasting components around.
It would do import Typography from '@material-ui/core/Typography/Typography'
.
After i fixed those to '@material-ui/core/Typography'
the mismatched style warnings where no more.
I hope this helps others.
Have same issue with next configuration:
"@material-ui/core": "^4.8.2",
"@material-ui/styles": "^4.8.2",
"jss": "10.0.2",
"react-jss": "^10.0.3",
"next": "^9.1.6",
I think because of this mismatch I get this css wrong render on SSR rendering:
Instead of:
The error for me looks like:
Warning: Prop
className
did not match. Server: "MuiButton-label makeStyles-buttonLabel-76 makeStyles-buttonLabel-160" Client: "MuiButton-label makeStyles-buttonLabel-76 makeStyles-buttonLabel-158"
Having the same issue as well:
"@material-ui/core": "^4.8.0",
"@material-ui/icons": "^4.5.1",
"@material-ui/lab": "^4.0.0-alpha.37",
"@material-ui/styles": "4.7.1",
Is this a known bug?
Is there an acceptable workaround for this at the moment? This brakes SSR for me which basically makes material-ui inviable because the app requires SSR.
@fullofcaffeine The workaround is not to use dynamic styles. This doesn't impact the core components, it would impact @material-ui/styles that accepts props and the Box, when used with JSS (no emotion or styled-components).
@oliviertassinari Thanks for the prompt reply.
As far as I understand, dynamic styles are pretty core to the mui customization workflow. I haven't paid much attention, but I think I'm using them heavily. Does the workaround you describe require me not to use makeStyles
/createStyles
on the server?
None of the core components use the dynamic styles yet because we have uncovered too many issues with them. I don't understand your question.
I don't use material, but use next and sc, so it's problem about them.
@oliviertassinari Sorry if I wasn't clear. Perhaps I don't understand exactly what you meant by dynamic styles. Is that the mechanism by which you customize the CSS by means of makeStyles
? If so, I use it all over the app to style it. Or are you referring to something else?
It's different, the issue is with https://material-ui.com/styles/basics/#adapting-based-on-props that we don't use with the core components yet because of a bunch of issues we have uncovered.
@oliviertassinari I'm not using this feature, yet I'm still getting a broken layout after the view is hydrated.
Hmm, it seems related to SSR and conditional expressions. If the JSX in question does not have any conditionals, then the styles don't brake when hydration is completed. However, if I add the following line:
{this.context.loggedIn && <Typography>LOGGED IN!</Typography>}
And reload, the HTML from the server correctly includes "LOGGED_IN", but then after hydrating, the whole style brakes, breaking the layout and the aforementioned error is displayed in the browser console.
Any insights?
Apologies, I finally found out why it was happening. Happens that on the SSR server, the context was not being loaded, so it was actually causing a mismatch of states between client<>server hydration. This was causing the bad class names, for some reason.
@fullofcaffeine Can I know what is the fix for this. Is going back to the older Material version the only fix as of now??
I am getting this,
index.js:1 Warning: Prop `className` did not match. Server: "MuiAvatar-root MuiAvatar-circle makeStyles-avatar-6621 MuiAvatar-colorDefault" Client: "MuiAvatar-root MuiAvatar-circle makeStyles-avatar-2 MuiAvatar-colorDefault"
Also, the fieldset
is not adding the little space behind the label unless I force it to hot-replace the component.
Basically https://material-ui.com/getting-started/templates/sign-in/ but with NextJS
Following this article, I managed to make it work but isn't straight forward, I hope it can help someone.
https://medium.com/manato/ssr-with-next-js-styled-components-and-material-ui-b1e88ac11dfa
Any news?
I have the feeling that this issue is going off track. The problem we are discussing is dynamic props usage that can sometimes cause an issue (strict mode issue). However there are a bunch of comments (and likely upvotes) that are unrelated. If you are facing this issue, and not using style functions (e.g only using the components), then you are facing a miss configuration issue. It's not the right place to ask for help. For instance, I was recently helping https://github.com/mui-org/material-ui/issues/18018#issuecomment-576373174 :).
what built in components are also dynamic? Box was mentioned above, are there others or am I misreading that?
I removed strict mode during development mode on the app i'm working on and that helped with this issue. interestingly with strict mode on, I was still getting this with CardMedia
component.
Following this article, I managed to make it work but isn't straight forward, I hope it can help someone.
https://medium.com/manato/ssr-with-next-js-styled-components-and-material-ui-b1e88ac11dfa
Thanks, @edgarcheverier!
This works for me after updating the _document.tsx
and _app.tsx
files.
I am using
"@material-ui/core": "^4.9.5",
"@material-ui/icons": "^4.9.1",
"next": "latest",
"react": "16.13.0",
"styled-components": "^5.0.1"
For me it was the automatic imports of my IDE after copy pasting components around.
It would doimport Typography from '@material-ui/core/Typography/Typography'
.
After i fixed those to'@material-ui/core/Typography'
the mismatched style warnings where no more.
I hope this helps others.
This fix helped me 馃憤
I'm having this issue again :(
But now it's related to the server having two rendering passes (AFAIK, that's how it should be) + the className suffix generation logic.
If anyone could have a look, the issue is described here (and here).
EDIT: No, it wasn't related to this either. Check the answer if you're curious
For me it was the automatic imports of my IDE after copy pasting components around.
It would doimport Typography from '@material-ui/core/Typography/Typography'
.
After i fixed those to'@material-ui/core/Typography'
the mismatched style warnings where no more.
I hope this helps others.
this helped me too, it was very tricky to find all imports but it seems like these imports affect the unrelated customized (styled) components on the page....
)im not sure its connected but @oliviertassinari i suggest you to look at this and it might also be the issue in this problem )
Hi @oliviertassinari I am also facing this issue of differnet classnames on server and at client side and I am passing props in many parts of my application. Is there any workaround available or I will have to stop passing props from the entire appication(that will be a very tedious task). Please help.
@oliviertassinari can you please look into it on priority if possible ? I am stuck right now. Will have to update the css from the whole application because of this issue.
@ankit-gupta1307 have tried to check in your whole application if u have imports with duplicate component directory like this example: import Typography from '@material-ui/core/Typography/Typography'
?
Yes @adir1661 I have looked into my application, don't have these kind of import statements.
same here, got the same bug and none of that double folder import.
I'm using dynamic styles .
Can anyone here help ?
Something really unexpected fixed the bug for me, hope this will be the same for everyone.
I replaced const css = sheets.toString();
and <style id="jss-server-side">${css}</style>
in HTML by ${sheets.getStyleElement()}
which generates the html tag.
After that, I don't have any more problems with dynamic styling in SSR.
Really hope this will fix the bug for all of you.
I have the feeling that this issue is going off track. The problem we are discussing is dynamic props usage that can sometimes cause an issue (strict mode issue). However there are a bunch of comments (and likely upvotes) that are unrelated. If you are facing this issue, and not using style functions (e.g only using the components), then you are facing a miss configuration issue. It's not the right place to ask for help. For instance, I was recently helping #18018 (comment) :).
Can you please explain that issue in detail - why is that happening? AFAIK React.StrictMode only highlights potential issues, but in this thread I see it even breaks the code. How?
AFAIK React.StrictMode only highlights potential issues, but in this thread I see it even breaks the code. How?
It highlights actual issues. It is intended that components inside React.StrictMode break if they're not concurrent safe.
An update, this issue is being resolved in v5 thanks to #22342 and the new @material-ui/styled-engine
package.
That's great @oliviertassinari will give it a try
Most helpful comment
thank you for reporting it. I'm experiencing the same issue here..