Single spa integration issue with material ui styles

Viewed 2684

We are trying to integrate Single spa with React and Material UI. We have 2 React applications with Material UI:

  1. The root container.
  2. A microfront (spa application).

When we integrate both components we have the following error:

It looks like there are several instances of @material-ui/styles initialized in this application. This may cause theme propagation issues, broken class names, specificity issues, and makes your application bigger without a good reason. See https://material-ui.com/r/styles-instance-warning for more info.

The configuration files are like this:

webpack.config. With this configuration we want to externalize so that it uses the material ui dependency present in the root container application.

webpack.config.js

index.js. We are registering the microfront.

index.js

index.html We are using the systemjs to load the material modules and the microfront.

enter image description here

We followed the steps suggested in this page without getting any satisfactory result:

https://material-ui.com/getting-started/faq/#i-have-several-instances-of-styles-on-the-page

2 Answers

I have the same setup as the above comment, but it did not fully solve the issue for me for multiple microfrontends. What did solve it for me was this:

I believe that each microfrontend is injecting its own stylesheet into the DOM, whether that's through calling makeStyles or not, I'm not entirely sure. You can see this in this example - https://d4e2yqa5hvu3s.cloudfront.net/ by inspecting the source and looking at the head tag - there are two MuiTypographys. The source code for that was linked in this article. What I did was I made sure that the import of the material ui components/styles were all consistent. So if you declare in your import map:

{
 "@material-ui/core": "<location of js>"
}

Then all imports of your components must follow:

import { x } from '@material-ui/core';

I also prefaced each style class with a unique name and seed per microfrontend (see above article and here). Like:

import { ThemeProvider, StylesProvider, createGenerateClassName } from '@material-ui/core';

const generateClassName = createGenerateClassName({
  disableGlobal: true,
  productionPrefix: 'foo', // defaults to 'jss'
  seed: 'bar', // defaults to ''
});
  ...
  <StylesProvider generateClassName={generateClassName}>
      <ThemeProvider theme={myTheme}>
  ...

I believe I have mitigated any issues (broken styling/propagation), and the warning about multiple styling on the page has now disappeared but if anyone has any more ideas, I'd be interested to hear them :)

I've also been struggling with material UI in Single Spa micro-frontends, since I also had multiple dependencies for material-ui, which caused random rendering errors.

In order to prevent that, you need to use material-ui as in-browser module, so you only have one copy that your micro-frontends will use. For this, it needs to be compatible with SystemJS. More info: https://single-spa.js.org/docs/recommended-setup/#systemjs

In order to do so, the easiest way is using UMD script. For example, if you are using @material-ui/core version 4.11.3, you will need this script:

"https://cdn.jsdelivr.net/npm/@material-ui/core@4.11.3/umd/material-ui.production.min.js"

You will have to load that script with the rest of your import maps:

for example:

    <script type="systemjs-importmap">
      {
        "imports": {
          "react": "https://cdn.jsdelivr.net/npm/react@17.0.1/umd/react.production.min.js",
          "react-dom": "https://cdn.jsdelivr.net/npm/react-dom@17.0.1/umd/react-dom.production.min.js",
          "single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.8.2/lib/system/single-spa.min.js",
          "rxjs": "https://cdn.jsdelivr.net/npm/@esm-bundle/rxjs@6.6.6/system/es2015/rxjs.min.js",
          "@material-ui/core": "https://cdn.jsdelivr.net/npm/@material-ui/core@4.11.3/umd/material-ui.production.min.js"
        }
      }
    </script>

Another possibility is using JSPM to load your dependencies as import maps. You can find an example here: https://github.com/jspm/project/issues/99

Finally, you can always pack your dependency in one of your shared components and share it from there. Since your shared components are in-browser modules, you will have only one copy.

I hope that was useful

Related