mui / material-ui

Material UI: Comprehensive React component library that implements Google's Material Design. Free forever.
https://mui.com/material-ui/
MIT License
93.43k stars 32.16k forks source link

generateClassName is not working for NextJS #17423

Closed kamalarieff closed 5 years ago

kamalarieff commented 5 years ago

Current Behavior 😯

When the generateClassName is used, it is not applied to the server rendered style sheets. It is only applied for the client side.

Expected Behavior 🤔

The class names are changed according to the generateClassName for both server side and client side.

Steps to Reproduce 🕹

Codesandbox: https://codesandbox.io/embed/github/kamalarieff/nextjs-mui-cls/tree/master/ Repo: https://github.com/kamalarieff/nextjs-mui-cls.git

I've added the StylesProvider as such. However, when I view the page source, I can't find my new class prefix anywhere:

Screen Shot 2019-09-14 at 5 05 27 PM

But when I do inspect element, I can find it:

Screen Shot 2019-09-14 at 5 09 16 PM

It seems like class name generator is applied to client side only.

My suspicion is that the instance of the class name generator is not the same for SSR and CSR but I don't know how to confirm this.

Solutions I've tried:

  1. https://material-ui.com/styles/api/#new-serverstylesheets-options
  2. Adding NODE_ENV=production to both the build and start step according to this issue
  3. Removing the
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: App => props => sheets.collect(<App {...props} />),
        });

    from pages/_document.js. This worked but there was a flash of unstyled content. There's also no documentation regarding enhanceApp so I wasn't sure about this implementation.

  4. Using the seed property works but there was also a flash of unstyled content.

Context 🔦

We have a separate package that provides react components and they are built with material ui. When I import this component into our app, their class names are conflicting with each other.

Your Environment 🌎

This repo was cloned from https://github.com/mui-org/material-ui/tree/master/examples/nextjs.

Dependencies:

"@material-ui/core": "latest",
"@material-ui/styles": "latest",
"clsx": "latest",
"next": "latest",
"prop-types": "latest",
"react": "latest",
"react-dom": "latest"
thminhVN commented 5 years ago

👍

oliviertassinari commented 5 years ago

You also need to apply the custom generator on the server-side:

_document.js


  const generateClassName = createGenerateClassName({
    productionPrefix: "yoghirt"
  });

  // Render app and page and get the context of the page with collected side effects.
  const sheets = new ServerStyleSheets({
    serverGenerateClassName: generateClassName
  });
kamalarieff commented 5 years ago

@oliviertassinari Awesome now it works. But I have a different issue now. After the app has been mounted, the server side style sheets are removed. But this only happens in my project though not the example project. Could one of the node_modules be affecting this?

oliviertassinari commented 5 years ago

@kamalarieff The server-side styles are meant to be removed, so the client can inject them back.

kamalarieff commented 5 years ago

@oliviertassinari I see. So does that mean the client is injecting partially empty styles?

kamalarieff commented 5 years ago

I found the issue. The styles disappear when I connect the application to redux.

I'm able to replicate the issue here.

kamalarieff commented 5 years ago

I've found the solution. For anyone reading, here it is.

Here's the repo for good measure.

@oliviertassinari the serverGenerateClassName was not mentioned in the docs. Might be a good idea to include them for future devs.

Romcol commented 4 years ago

I've found the solution. For anyone reading, here it is.

Here's the repo for good measure.

@oliviertassinari the serverGenerateClassName was not mentioned in the docs. Might be a good idea to include them for future devs.

@oliviertassinari Thanks, I really think the serverGenerateClassName needs to be documented. I spent an hour looking for this. The docs suggest that options should be sent like for StylesProvider. Thank you for the lib by the way :)

Jack-Works commented 4 years ago
serverGenerateClassName

Should add it to the document 👀

Jack-Works commented 4 years ago

🤣 really should add it to the document, wasted another 10 minutes today

aamirafridi commented 4 years ago

Important thing is that don't use the same instance of generateClassName on client and server

wrong ❌

const classPrefix = "my-prefix";
const generateClassName = createGenerateClassName({
    seed: classPrefix,
});

...
<StylesProvider generateClassName={generateClassName}>..</StylesProvider>

// on server
new ServerStyleSheets({
    serverGenerateClassName: generateClassName,
});

correct ✅

const classPrefix = "my-prefix";
const generateClassName = () => createGenerateClassName({
    seed: classPrefix,
});

...
<StylesProvider generateClassName={generateClassName()}>..</StylesProvider>

// on server
new ServerStyleSheets({
    serverGenerateClassName: generateClassName(),
});

More here

motevallian commented 3 years ago

Important thing is that don't use the same instance of generateClassName on client and server

wrong ❌

const classPrefix = "my-prefix";
const generateClassName = createGenerateClassName({
    seed: classPrefix,
});

...
<StylesProvider generateClassName={generateClassName}>..</StylesProvider>

// on server
new ServerStyleSheets({
    serverGenerateClassName: generateClassName,
});

correct ✅

const classPrefix = "my-prefix";
const generateClassName = () => createGenerateClassName({
    seed: classPrefix,
});

...
<StylesProvider generateClassName={generateClassName()}>..</StylesProvider>

// on server
new ServerStyleSheets({
    serverGenerateClassName: generateClassName(),
});

More here

Could you be more specific? How come you could use a SINGLE instance in both server and client? These are separate machines and hence separate instances. I think what you mean is to make sure every time the server receives a request, it creates a new generator, otherwise independent request will share the same generator, causing wrong classNames generated for any request other than the first time.

nazarien commented 2 years ago

Hello @oliviertassinari, I'm trying to add productionPrefix (and for seed it is working the same) to my project (already added createGenerateClassName into the document (server) and app (client)). But I get new classes ever page reload for example "myprefix5" -> "myprefix6". Have you met this issue?

I've found the same reported issue mui-org/material-ui#17423

nazarien commented 2 years ago

Hello @oliviertassinari, I'm trying to add productionPrefix (and for seed it is working the same) to my project (already added createGenerateClassName into the document (server) and app (client)). But I get new classes ever page reload for example "myprefix5" -> "myprefix6". Have you met this issue?

I've found the same reported issue mui-org/material-ui#17423

I've found the reason. Details here https://github.com/vercel/next.js/discussions/10898#discussioncomment-1728514 createGenerateClassName instance should be initialized in getInitialProps. Not obvious way and it's not clear from docs