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

[styles] Strict Mode support #18018

Closed
bmvantunes opened this issue Oct 24, 2019 · 49 comments
Closed

[styles] Strict Mode support #18018

bmvantunes opened this issue Oct 24, 2019 · 49 comments
Labels
bug 🐛 Something doesn't work package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. priority: important This change can make a difference
Milestone

Comments

@bmvantunes
Copy link

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"

Current Behavior 😯

Looks like the classNames generated at server rendering are different to the ones generated at browser rendering time.

Expected Behavior 🤔

Server-side rendering and client-side rendering should generate the same class names.

Steps to Reproduce 🕹

Steps (Code Sandbox):

  1. https://codesandbox.io/s/nextjs-hge3h:
  2. Open developer tools (F12) and refresh code sandbox:
  3. You'll see:
    Warning: Prop className did not match. Server: "MuiSvgIcon-root makeStyles-lightBulb-47" Client: "MuiSvgIcon-root makeStyles-lightBulb-48"

Steps (Running locally)

  1. git clone https://github.com/bmvantunes/material-ui.git
  2. cd material-ui/examples/nextjs
  3. npm install
  4. npm run dev
  5. Open the browser, F12 and you'll see the error:
    Warning: Prop className did not match. Server: "MuiSvgIcon-root makeStyles-lightBulb-47" Client: "MuiSvgIcon-root makeStyles-lightBulb-48"

Context 🔦

The reproduction of the error will look something like:
next-material

The only difference between my repository (example) and material-ui/examples/nextjs is the React.StrictMode instead of React.Fragment inside _app.js

Your Environment 🌎

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.

@bmvantunes bmvantunes changed the title Warning: Prop className did not match. [nextjs] Warning: Prop className did not match. Oct 24, 2019
@oliviertassinari oliviertassinari added the support: question Community support but can be turned into an improvement label Oct 24, 2019
@bmvantunes
Copy link
Author

@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

@oliviertassinari oliviertassinari removed the support: question Community support but can be turned into an improvement label Oct 24, 2019
@amiral-jion
Copy link

thank you for reporting it. I'm experiencing the same issue here..

@amiral-jion
Copy link

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

@bmvantunes
Copy link
Author

@amiral-jion it's good to know that it's a bug introduced in one of the new versions :)

@eps1lon eps1lon added bug 🐛 Something doesn't work package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. labels Oct 25, 2019
@oliviertassinari oliviertassinari self-assigned this Oct 25, 2019
@oliviertassinari
Copy link
Member

oliviertassinari commented Oct 25, 2019

Could it be a recent regression linked to a dependency/transitive dependency?

@oliviertassinari oliviertassinari removed their assignment Oct 25, 2019
@oliviertassinari
Copy link
Member

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.

@oliviertassinari oliviertassinari added the priority: important This change can make a difference label Oct 25, 2019
@oliviertassinari oliviertassinari removed the priority: important This change can make a difference label Dec 1, 2019
@diolar
Copy link

diolar commented Dec 6, 2019

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.

sarukuku added a commit to sarukuku/mis that referenced this issue Dec 29, 2019
The All toggle doesn't render correctly due to this issue --> mui/material-ui#18018. I've set the All toggle to render only when there are more than one reporters. This fixes the visual render for now but leaves the warning about the className mismatch between server and the client in the console.
@sashberd
Copy link

sashberd commented Jan 6, 2020

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:
Screen Shot 2020-01-06 at 10 07 21
Instead of:
Screen Shot 2020-01-06 at 10 08 09

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"

@fullofcaffeine
Copy link

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?

@fullofcaffeine
Copy link

fullofcaffeine commented Jan 9, 2020

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.

@oliviertassinari
Copy link
Member

oliviertassinari commented Jan 9, 2020

@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).

@fullofcaffeine
Copy link

@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?

@oliviertassinari
Copy link
Member

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.

@ziimakc
Copy link

ziimakc commented Jan 10, 2020

I don't use material, but use next and sc, so it's problem about them.

@fullofcaffeine
Copy link

@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?

@oliviertassinari
Copy link
Member

oliviertassinari commented Jan 10, 2020

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.

@fullofcaffeine
Copy link

@oliviertassinari I'm not using this feature, yet I'm still getting a broken layout after the view is hydrated.

@ankit-gupta1307
Copy link

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.

@ankit-gupta1307
Copy link

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

@adir1661
Copy link

@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' ?

@ankit-gupta1307
Copy link

Yes @adir1661 I have looked into my application, don't have these kind of import statements.

@hems
Copy link

hems commented Jun 12, 2020

same here, got the same bug and none of that double folder import.

I'm using dynamic styles .

@ankit-gupta1307
Copy link

Can anyone here help ?

@SimonGalet
Copy link

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.

richardbristow added a commit to richardbristow/event-auditor that referenced this issue Jul 29, 2020
Commented out StrictMode due to warnings in material-ui created from page dropdown in the table.
See mui/material-ui#18018 and mui/material-ui#20012
@oliviertassinari oliviertassinari removed their assignment Sep 8, 2020
@vanyadymousky
Copy link

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?

@eps1lon
Copy link
Member

eps1lon commented Sep 23, 2020

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.

@oliviertassinari
Copy link
Member

An update, this issue is being resolved in v5 thanks to #22342 and the new @material-ui/styled-engine package.

@ankitLV
Copy link

ankitLV commented Nov 12, 2020

That's great @oliviertassinari will give it a try

@Robert-OP

This comment has been minimized.

@SC0d3r
Copy link

SC0d3r commented Jan 20, 2021

Hi guys! I am using nextjs and material ui, I read all of the previous comments and nothing worked for me, then changed to v5 of material ui and still get this error
Prop className did not match. Server: "PrivateNotchedOutline-legend-11" Client: "PrivateNotchedOutline-legend-2"
is there anything I can do to solve this issue ?

@oliviertassinari
Copy link
Member

@SC0d3r StrictMode should be fixed with #24405.

@milindvishnoi

This comment has been minimized.

@oliviertassinari oliviertassinari added this to Q2 2021 Apr - Jun in MUI Core public roadmap Apr 5, 2021
@YuhuiRainie
Copy link

Hi Guys,

I encountered this issue with
"@material-ui/core": "3.9.3", "@material-ui/icons": "^4.11.2", "next": "10.1.3", "react": "17.0.2", "react-dom": "17.0.2", "url-loader": "^4.1.1"

I encountered this issue with AppBar in Material UI.
I am using this template: https://codesandbox.io/s/9wup7
After I delete <Typography className={classes.title} variant="h6" noWrap> Material-UI </Typography> <div className={classes.search}> <div className={classes.searchIcon}>

after I deleted these ClassName and write inline styles, it solves the problem.

@oliviertassinari
Copy link
Member

An update, we have now made enough progress with the new @material-ui/styled-engine package in v5 to move toward a progressive removal of the @material-ui/styles package (based on JSS). The current plan:

  • In v5.0.beta.0, this package will come standalone, in complete isolation from the rest.
  • In v5.0.0, this package will be soft deprecated, not promoted in the docs, nor very actively supported.
  • During v5, work on making the migration easier to the new style API (sx prop + styled() API). We might for instance, invest in the documentation for using react-jss that has more or less the same API. We could also invest in an adapter to restore the previous API but with emotion, not JSS.
  • In v6.0.0, this package will be discontinued (withStyles/makeStyles API).

This was made possible by the awesome work of @mnajdova.

richardbristow added a commit to richardbristow/event-auditor that referenced this issue May 14, 2021
Commented out StrictMode due to warnings in material-ui created from page dropdown in the table.
See mui/material-ui#18018 and mui/material-ui#20012
@prvnsmpth
Copy link

Just in case this helps someone: I was able to get rid of the prop className did not match error by simply replacing Box with a div.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work package: styles Specific to @mui/styles. Legacy package, @material-ui/styled-engine is taking over in v5. priority: important This change can make a difference
Projects
None yet
Development

No branches or pull requests