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

[styled-engine-sc] MUI + styled-components + Next.js CSS rules ordering issue #29742

Open
2 tasks done
zibra opened this issue Nov 17, 2021 · 59 comments
Open
2 tasks done
Assignees
Labels
bug 🐛 Something doesn't work external dependency Blocked by external dependency, we can’t do anything about it nextjs package: styled-engine-sc Specific to styled-components priority: important This change can make a difference

Comments

@zibra
Copy link

zibra commented Nov 17, 2021

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Current behavior 😯

Next.js projects using MUI + styled-components based on the example configuration from:\ have incorrect MUI components styling on production. The order of the CSS rules is different from SSR and after hydration.

Expected behavior 🤔

UI elements looks always same (on dev run and on production). CSS rules are applied always in the same order.

Steps to reproduce 🕹

Steps:

  1. visit https://codesandbox.io/s/heuristic-framework-2leki?file=/pages/about.tsx
  2. refresh preview after server build and run the prod bundle
  3. navigate between pages + play with refreshing pages (to load SSR styles)

Video preview:
https://www.youtube.com/watch?v=A0dqNA8IO4Q

Context 🔦

When I was testing it locally it happened only on production builds, but in code sandbox it happens also on development mode. Just css class order is different between dev and prod.

Privided code sanbox is a fork for from official example from: https://github.com/mui-org/material-ui/tree/master/examples/nextjs-with-styled-components-typescript

I've just added 2 more buttons.

There are different states for the buttons depending on the scenario:
Correct styling:
obraz

Incorrect styling:
obraz

CSS rules order comparison:
obraz

Your environment 🌎

I've tested this issue on Windows 10, Mac OS, AWS Amplify and at the end I've found that it also happens on CodeSandbox example. Always with latest versions with MUI, Next, node and styled-components

@zibra zibra added the status: waiting for maintainer These issues haven't been looked at yet by a maintainer label Nov 17, 2021
@zibra zibra changed the title MUI + styled-components + Next.js CSS rules ordering issue MUI + styled-components + Next.js CSS rules ordering issue on prod Nov 17, 2021
@zibra zibra changed the title MUI + styled-components + Next.js CSS rules ordering issue on prod MUI + styled-components + Next.js CSS rules ordering issue Nov 18, 2021
@zibra
Copy link
Author

zibra commented Nov 18, 2021

Today I've tried same code with example using emotion (no styled-components and I was not able to reproduce such issues there.
Code sandbox: https://codesandbox.io/s/gracious-blackburn-u4rw1?file=/pages/about.tsx

Maybe it's styled-components issue or configuration for SC

@zibra
Copy link
Author

zibra commented Nov 18, 2021

I've tried wrapping components tree with StyledEngineProvider with injectFirst in _app.tsx but it's making no difference

@zibra
Copy link
Author

zibra commented Nov 18, 2021

@mnajdova could you please take a look at this one?

@cjmcintyre
Copy link

cjmcintyre commented Nov 19, 2021

Maybe it's styled-components issue or configuration for SC

+1 this, found I didn't remove emotion completely and added in the required to my package.json (I'm using yarn if it makes any difference here)

"@mui/styled-engine": "npm:@mui/styled-engine-sc@latest",
"resolutions": {
  "@mui/styled-engine": "npm:@mui/styled-engine-sc@latest"
}

Now upon a deployment I've found my emotion is correctly removed but I have now styling inconsistencies.

Edit:
Upon further investigation i'm getting the following.

It looks like there are several instances of 'styled-components' initialized in this application. This may cause dynamic styles to not render properly, errors during the rehydration process, a missing theme prop, and makes your application bigger without good reason.

https://styled-components.com/docs/faqs#why-am-i-getting-a-warning-about-several-instances-of-module-on-the-page

Haven't checked if this is occurring in a fresh NextJs, Material ui, styled-components but is probably my next step.

Edit again:
I'm unsure on how to resolve my webpack issues as based on my reading the advice in the above link webpack.optimize.CommonsChunkPlugin was deprecated v4 and nextjs is default v5..

Further investigation found #24109 which the moral of the story is inconsistencies with styling in injection order due to emotion / styled-components and seems to have been removed from the milestone v5.1

@zibra
Copy link
Author

zibra commented Nov 19, 2021

For now the only solution that I've found, was to switch my whole project to emotion...

@cjmcintyre
Copy link

For now the only solution that I've found, was to switch my whole project to emotion...

@zibra not my favourite resolution but also the direction I'll probably take.

@AndyW22
Copy link

AndyW22 commented Nov 22, 2021

This is the same issue as #29210 , the problem is the compatibility with the Link component. Using an <a> tag instead fixes the problem. Though you will obviously lose the benefits of the Link component.

@cjmcintyre
Copy link

This is the same issue as #29210 , the problem is the compatibility with the Link component. Using an <a> tag instead fixes the problem. Though you will obviously lose the benefits of the Link component.

@AndyW22 Is it the same problem?

I noticed in your example you don't use the provided example from material so therefore you are lacking the custom Link component. that they provide. In the sandbox url above the Link component provided by the example is the one being utilised.

@AndyW22
Copy link

AndyW22 commented Nov 23, 2021

This is the same issue as #29210 , the problem is the compatibility with the Link component. Using an <a> tag instead fixes the problem. Though you will obviously lose the benefits of the Link component.

@AndyW22 Is it the same problem?

I noticed in your example you don't use the provided example from material so therefore you are lacking the custom Link component. that they provide. In the sandbox url above the Link component provided by the example is the one being utilised.

unfortunately their example repo doesnt actually use styled components at all, it simply sets it up in the project but doesnt use the styled function to actually make a styled component. I tried adding the CardContainer onto their example repo in the /about page and causes the same issue, so based on that their custom Link component doesn't fix this.

I also added it to the sandbox example and the same issue occurs at the same time as the other styling bugs, and, like with my example, using an <a> tag fixes the problem. I think it's most likely the same issue given on how they happen at the same time, styles have similar issues, they both seem to be caused by the Link component, and both fixed by the <a> tag.

@mnajdova mnajdova self-assigned this Nov 23, 2021
@mnajdova
Copy link
Member

I will look more into this tomorrow and post my findings

@mnajdova
Copy link
Member

I spent some time on this today, but haven't resolved it. At the start I thought the issue is that the babel-plugin-styled-components is not picking up the styled() coming from mui.

On this matter, I found the topLevelImportPaths option thanks to https://xstyled.dev/docs/migrate-from-styled-components/#babel-plugin (thank you @gregberge :))

I've tried updating the .babelrc to:

.babelrc

{
  "presets": ["next/babel"],
  "plugins": [
    [
      "babel-plugin-styled-components",
      {
        "topLevelImportPaths": [
          "@mui/material",
          "@mui/material/*",
          "@mui/system",
          "@mui/styled-engine-sc"
        ]
      }
    ]
  ]
}

There were some issues thrown at the start, that indicated that withConfig does not exists, so I believe the plugin started to pick up the imports from the styled() utility. I created #29873 and tried to use the packages build from it:

package.json

{
  "name": "nextjs-with-styled-components-typescript",
  "version": "5.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@mui/material": "https://pkg.csb.dev/mui-org/material-ui/commit/97b8bd28/@mui/material",
    "@mui/styled-engine-sc": "https://pkg.csb.dev/mui-org/material-ui/commit/97b8bd28/@mui/styled-engine-sc",
    "next": "latest",
    "next-transpile-modules": "latest",
    "react": "latest",
    "react-dom": "latest",
    "react-is": "latest",
    "styled-components": "latest"
  },
  "devDependencies": {
    "@types/react": "latest",
    "@types/styled-components": "latest",
    "babel-plugin-styled-components": "latest",
    "typescript": "latest"
  }
}

This did not work too. I even tried transpaling the mui packages using:

next.config.js

const path = require('path');
const withTM = require('next-transpile-modules')(['@mui/material', '@mui/system']) // pass the modules you would like to see transpiled

/** @type {import('next').NextConfig} */
module.exports = withTM({
  reactStrictMode: true,
  webpack: (config) => {
    config.resolve.alias = {
      ...config.resolve.alias,
      '@mui/styled-engine': '@mui/styled-engine-sc',
    };
    config.module = {
      ...config.module,
      rules: [
        {
          test: /\.js|jsx|ts|tsx$/,
          use: {
            loader: 'babel-loader',
            options: {
              presets: ['next/babel'],
              plugins:  [
                [
                  "babel-plugin-styled-components",
                  {
                    "topLevelImportPaths": [
                      "@mui/material",
                      "@mui/material/styles",
                      "@mui/system",
                      "@mui/styled-engine-sc",
                      "@mui/styled-engine"
                    ],
                    "ssr": true
                  }
                ]
              ],
              include: [
                  path.resolve('*'),
                  path.resolve('node_modules/@mui/'),
              ],
              exclude: /node_modules\/(?!@mui).+/
            }
          }
        }
      ]
    }
    return config;
  },
})

But with no luck. the issue was still there. If someone wants to push further the investigation, make sure to use the packages from #29873, for example:

https://pkg.csb.dev/mui-org/material-ui/commit/97b8bd28/@mui/material
https://pkg.csb.dev/mui-org/material-ui/commit/97b8bd28/@mui/styled-engine-sc

I've pushed what I've tried in #29873

@mnajdova
Copy link
Member

cc @mui-org/core maybe someone will have a better idea than me

@mnajdova
Copy link
Member

@pedroeckel
Copy link

I have the same problem on my project

@mnajdova
Copy link
Member

One more update on the test project in #29873

I've copied our Typography component from mui locally there (I literally just changed the imports) and even added the custom styled.js utility we have in mui. The local copy of the Typography works, but the one imported from mui doesn't so I expect it is still something related to the plugin config. 🤦

@mnajdova
Copy link
Member

I don't think I will have much more time to spend on this, but if someone from the community wants to help I can provide some guidance.

@cjmcintyre
Copy link

I don't think I will have much more time to spend on this, but if someone from the community wants to help I can provide some guidance.

@mnajdova thanks for looking into it! Just had a busy week at work myself, I'll look into what you've done over the weekend and might touch base if you don't mind.

@kondei
Copy link

kondei commented Jan 26, 2022

  • styled-components@5.3.0 (not latest)
  • const withTM = require('next-transpile-modules')(['@mui/material', '@mui/lab', '@mui/system']); in next.config.js
  • experimental: { styledComponents: true,}, in next.config.js

seems to fix my case, but I cannot be sure.
I want it to be fixed without complex settings.

@kondei
Copy link

kondei commented Jan 26, 2022

styled-components issue says that some SSR problems were fixed since 5.3.1.
But it seems to break some styles since 5.3.1 with MUI & next.js build.
I am confused.

styled-components/styled-components#3482

@kdelmonte
Copy link

I am facing the same issue. Is there anything one can do to fix it in the meantime?

@mnajdova
Copy link
Member

mnajdova commented Mar 2, 2022

@kdelmonte we haven't find any solution to this problem yet, and it looks like there is no response to:

@kdelmonte
Copy link

Thank you @mnajdova. I migrated yesterday to emotion because I could not wait anymore and like you say, the migration was pretty easy. Thanks again.

@mnajdova
Copy link
Member

mnajdova commented Mar 2, 2022

Sure thing @kdelmonte, and sorry for the delayed answer.

@rbong
Copy link

rbong commented Jul 30, 2022

@kdelmonte we haven't find any solution to this problem yet, and it looks like there is no response to:

I have a PR up to fix styled-components/babel-plugin-styled-components#365: styled-components/babel-plugin-styled-components#380

However I haven't made a fix for styled-components/babel-plugin-styled-components#364.
The second test case already works.
I'm not sure if the first test case should be fixed.
It seems like styled-components intentionally ignores renamed imports of styled because of styled-components/babel-plugin-styled-components#224.

@mnajdova
Copy link
Member

mnajdova commented Aug 5, 2022

@rbong I haven't been looking into this for quite some time. I can try to refresh my view on it next week and provide more feedback if I can. Thanks a lot for the effort put there 🙏

@donhessman
Copy link

@mnajdova Any new updates on this issue?

@mnajdova
Copy link
Member

As far as I know, we are still blocked by styled-components/babel-plugin-styled-components#365 and styled-components/babel-plugin-styled-components#364. There wasn't much activity there honestly, I left now comment on one of the issues. Haven't tested with nextjs 13 if that makes any difference, but I doubt honestly.

@jonborchardt
Copy link

Any new updates on this issue?

@landsman
Copy link

Hello, is there a plan how to fix this? 🙏

@CarlosHenriqueMkt
Copy link

Nota: Essa solução foi compartilhada comigo por meio de um colega de trabalho. Vale a pena o teste.

Solução:
A solução para esse problema é usar o plugin "@emotion/cache" do Emotion e passá-lo para o Material UI ThemeProvider. Isso permitirá que o Material UI e o Emotion compartilhem o mesmo cache de estilos e resolvam o conflito de classes geradas.

Aqui está um exemplo de como fazer isso:

  1. Instale o "@emotion/cache" e "@emotion/react" no seu projeto:

npm install @emotion/cache @emotion/react
ou
yarn add @emotion/cache @emotion/react

  1. Altere o arquivo "_document.js" na pasta "pages" do seu projeto Next.js e adicione o seguinte código:
import createCache from "@emotion/cache";
import { CacheProvider } from "@emotion/react";
import Document, { Head, Html, Main, NextScript } from "next/document";
import { ServerStyleSheets } from "@material-ui/core/styles";

export default function MyDocument() {
  return (
    <Html>
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

MyDocument.getServerSideProps = async (ctx) => {
  // Cria um novo cache para o Emotion
  const cache = createCache({ key: "css" });
  cache.compat = true;

  // Cria uma nova instância de ServerStyleSheets para o Material-UI
  const sheets = new ServerStyleSheets();

  const originalRenderPage = ctx.renderPage;

  // Renderiza a página com o Emotion e o Material-UI
  ctx.renderPage = () =>
    originalRenderPage({
      enhanceApp: (App) => (props) =>
        (
          <CacheProvider value={cache}>
            {sheets.collect(<App {...props} />)}
          </CacheProvider>
        ),
    });

  // Busca as props iniciais da página usando o getInitialProps padrão do Document
  const initialProps = await Document.getInitialProps(ctx);

  // Adiciona o estilo do Material-UI coletado pelo ServerStyleSheets na propriedade styles do objeto de retorno
  return {
    ...initialProps,
    styles: (
      <>
        {initialProps.styles}
        {sheets.getStyleElement()}
      </>
    ),
  };
};
  1. Em seguida, envolva o component com o "CacheProvider" do Emotion no seu arquivo "_app.js". Caso esteja usando o ThemeProvider, basta envolve-lo também:
import { CacheProvider } from "@emotion/react";
import CssBaseline from "@material-ui/core/CssBaseline";
import createCache from "@emotion/cache";
import { useEffect } from "react";

// Cria um novo cache para o Emotion
const cache = createCache({ key: "css" });
cache.compat = true;

function MyApp({ Component, pageProps }) {
  // Remove o estilo do Material-UI adicionado pelo servidor
  useEffect(() => {
    const jssStyles = document.querySelector("#jss-server-side");
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);
  return (
    // Provê o cache do Emotion para a aplicação
    <CacheProvider value={cache}>
        {/* Reseta o CSS */}
        <CssBaseline />
        {/* Renderiza o componente */}
        <Component {...pageProps} />
    </CacheProvider>
  );
}

export default MyApp;

Essas etapas devem ajudar a resolver o problema de incompatibilidade entre o Material UI, o Emotion e o Next.js SSR.

Vale ressaltar que estou compartilhando com o intuito de ajudar quem chegou agora e está procurando essa resposta. Caso haja algum erro só informar que eu edito o post para corrigir.

@donhessman
Copy link

@mircea-dinoiu that is really encouraging news! Any chance you could share some code examples of your working setup? Particularly your _document.tsx (or ThemeRegistry.tsx if you're using the app directory).

Even after upgrading to styled-components v6 I still get the className mismatch hydration warning. I'm curious to see what the trick is.

@mnajdova
Copy link
Member

I tried creating a brand new repository using Next.js 13, Material UI + styled-components v6, but the same problems arise. This is the testing repo: https://github.com/mnajdova/nextjs13-material-ui-styled-components.

Unfortunately, we still need to apply the isServer hack described in #29742 (comment) (check the next.config.js from the repo above).

If someone is interested in looking into more details, check the repository and maybe we can start with opening issues about the specific mentioned in #29742 (comment) this time on next.js repository, as they are the one who offers now in house support for this in their config.

@0x-xsh

This comment was marked as off-topic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work external dependency Blocked by external dependency, we can’t do anything about it nextjs package: styled-engine-sc Specific to styled-components priority: important This change can make a difference
Projects
None yet
Development

No branches or pull requests