Material-ui: Duplicated MuiButtonBase (and others) in <style> elements

Created on 7 May 2019  路  32Comments  路  Source: mui-org/material-ui


The <head> section contains several times the same material-ui internal <style> creating conflicts in styles being applied. The content of those <style> are not using unique names as before it seems: .jss346...

  • [x] This is not a v0.x issue.
  • [x] I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior 馃

For example, .MuiButtonBase-root should be present once only.

Current Behavior 馃槸

.MuiButtonBase-root is added 2/3 times.

material-ui-issue

material-ui-style-content

Steps to Reproduce 馃暪


Cannot reproduce with a simple piece of code; even in our application sometimes it's ok sometimes not. Tell us how to troubleshoot on our side to provide more information.

Context 馃敠


Nothing special I believe. Components are badly rendered because of material-ui internal styles are being applied out of order.

Your Environment 馃寧


This issue appeared since v4 beta.0 (still in beta.1): v4 alpha were ok.

| Tech | Version |
|--------------|---------|
| Material-UI | v4 beta.0 and 1 |
| React | 16.8.6 |
| Browser | |
| TypeScript | 3.4.5 |
| etc. | |

incomplete

Most helpful comment

@marc-polizzi Be sure that your path imports are not too deep.

import { Button } from '@material-ui/core'; // Ok
import Button from '@material-ui/core/Button'; // Ok
import Button from '@material-ui/core/Button/Button'; // Not Ok

If you're building with wepack you could use something like webpack-bundle-analyzer or put the created stats.json into webpack-analyse to see which modules are importing creating the duplicate module.

All 32 comments

@marc-polizzi How many theme providers do you use?

@oliviertassinari the application is using several

<ThemeProvider ...

statements but in the faulty usecases we're using a single one. I've put a breakpoint in the Chrome browser and only the one of our top level component seems called. Something like:

    render() {
        return (
            <React.Fragment>
                <CssBaseline/>
                <ThemeProvider theme={UxTheme}>
                    <UxConsoleApp />
                </ThemeProvider>
            </React.Fragment>
        );
    }

@marc-polizzi Two things. If you are using multiple ThemeProvider, you need to provide one at the root. You can change the class name generation to output non-global class names with the disableGlobal option, like before in v3.

@marc-polizzi Do you use multiple React mount point?

If you mean we're calling several times ReactDOM.render(...), yes: one for the main application component and others for rendering dialogs. For the sake of simplicity, I've removed all of them to keep the only one in ourindex.tsx file that is rendering our top level component:

ReactDOM.render(app, document.getElementById('ic3-root') as HTMLElement);

Still have the same issue.

@marc-polizzi Then I'm out of ideas. You need to run a dichotomy on your React Tree.

@oliviertassinari sorry not sure to understand; what do you mean by "run a dichotomy" ?

You can use a dichotomy to find the source of the problem. Unmount half your tree, see if the problem is still present, repeat.

@marc-polizzi Let us know when you find the source of the problem. We would be happy to understand how we can improve the implementation. I'm closing, waiting for a reproduction. Thanks for the report.

Here is a little example - self contained in the index.tsx file - that is failing in my project. Unfortunately, I cannot reproduce the same behavior in codesandbox.io. When running the following React app, I can see several MuiButtonBase CSS rules generated creating some unexpected conflicts.

It seems this is related to the import:

import ButtonBase from '@material-ui/core/ButtonBase/ButtonBase';

Importing as following is solving the issue:

import ButtonBase from '@material-ui/core/ButtonBase';

I'm still investigating... Any advice / feedback?

import * as React from 'react';
import * as ReactDOM from 'react-dom';

import IconButton from '@material-ui/core/IconButton';
import ButtonBase from '@material-ui/core/ButtonBase/ButtonBase';

class UxMainApp extends React.Component {

    render() {

        return (
            <>
                <div style={{border: '1px solid black'}}>
                    <IconButton>clik-me</IconButton>
                </div>
                <ButtonBase component={'a' as any}> app-link </ButtonBase>
            </>
        );

    }

}

ReactDOM.render(<UxMainApp/>, document.getElementById('ic3-root') as HTMLElement);

@marc-polizzi You should get a style module duplication warning in the console in this case with v4.0.0-beta.1. You might bundle two versions of the components: the CJS and ESM.

You're right, there seems to be two modules ButtonBase as you can see in the browser tools window attached. But the console is empty.

modules

I fear we can't help more without a reproduction.

I've copy / pasted that file index.tsx into a codesandbox.io but got no issue. I guess something is wrong with our project configuration but have no idea where to look at. In the meantime, we've added a prebuild script that is grepping our files for this kind of imports.

@marc-polizzi Be sure that your path imports are not too deep.

import { Button } from '@material-ui/core'; // Ok
import Button from '@material-ui/core/Button'; // Ok
import Button from '@material-ui/core/Button/Button'; // Not Ok

If you're building with wepack you could use something like webpack-bundle-analyzer or put the created stats.json into webpack-analyse to see which modules are importing creating the duplicate module.

I have tried to reproduce the style duplication warning without luck: https://codesandbox.io/s/affectionate-ramanujan-mnjwg.

"dependencies": {
    "@material-ui/core": "^4.0.1",
    "@material-ui/icons": "^4.0.1",
    "classnames": "^2.2.6",
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
...
}

I'm having this same issue. .MuiButtonBase-root is being applied twice and is overriding any other styling.
Interestingly, this will only happen some of the time. We have a component, "AccountDetails" that is passed some props and then displays the account information. Some accounts never have this issue while other accounts always do. It's the same component, just different account detail props.

I have checked the "classes" and "theme" props and those do not appear to change between different account detail views nor after the component mounts.
We are using '@material-ui/core/CssBaseline';

The MUI code:
```import { Button, Grid, List, ListItem, Paper, Tooltip, Typography, Fab, MenuItem, MenuList } from '@material-ui/core';
import { Button, Grid, List, ListItem, Paper, Tooltip, Typography, Fab, MenuItem, MenuList } from @material-ui/core';
import { withStyles } from '@material-ui/core/styles';
...
const styles = theme => ({
extendedIcon: {
marginRight: theme.spacing(1)
},
paper: {
style: { display: 'flex', flexWrap: 'wrap', textAlign: 'center', justifyContent: 'center' }
},
menuStyles: {
display: 'flex',
flexDirection: 'column',
width: '100%',
backgroundColor: 'ghostwhite',
border: '1px solid darkgray',
margin: '8px 0px'
},
disabled: {
cursor: 'default',
pointerEvents: 'none'
},
fabStyle: {
'&:hover': { backgroundColor: 'transparent' }
}
});

export default flowRight(
connect(
mapStateToProps,
mapActionsToProps
),
withStyles(styles, { withTheme: true })
)(AccountDetails);

These are the exact same component. Is there anything aside from the "classes" and "theme" props that might cause this?

<img width="853" alt="Screen Shot 2019-06-12 at 4 17 46 PM" src="https://user-images.githubusercontent.com/29046393/59393128-2679b300-8d2f-11e9-8a76-75660b6d2f8f.png">


Also, In our components that use MUI's ExpansionPanel component, the .MuiButtonBase-root class is applied again and overrides the MUI's own .MuiExpansionPanelSummary-root styling. 
We have not added any custom styling to this ExpansionPanel component

import { ExpansionPanel, ExpansionPanelSummary, ExpansionPanelDetails, Paper } from '@material-ui/core';
import { ExpandMore } from '@material-ui/icons';
...
}>
```

Screen Shot 2019-06-12 at 4 09 35 PM

This:
Screen Shot 2019-06-12 at 4 53 23 PM
vs this:
Screen Shot 2019-06-12 at 4 53 15 PM

One of our dependencies also had material-ui as a dependency. We changed material-ui to a peer dependency and our issues are now fixed!

Just cross-posting https://github.com/mui-org/material-ui-pickers/issues/1143#issuecomment-511525960 here, in case it helps anyone.

I got duplicate style tags, which caused some styles to be wrong (in my case, styles from the @material-ui/pickers package. Following the recommendation at https://github.com/mui-org/material-ui/issues/15610#issuecomment-492215308 solved my issue.

I think it might be possible to use the no-restricted-imports eslint rule with a glob pattern to warn against this in the future. Or, in the case of TSLint, create a regex that matches duplicate strings (with import-blacklist. Unfortunately my Regex-fu is not strong enough.


Original comment

I've experienced the same issue, tracked the cause to this: https://github.com/mui-org/material-ui/issues/15610. I followed https://github.com/mui-org/material-ui/issues/15610#issuecomment-492215308, and fixed all imports that was double nested, eg. changed

import Button from '@material-ui/core/Button/Button';

to

import Button from '@material-ui/core/Button';

I had a LOT of imports to fix (did a search on from '@material-ui/core/, it was a breeze). After all imports were fixed, the problem went away.

You can check if you have the same problem with duplicate imports, by going in to your Chrome DevTools > Sources > expand localhost:port > node_modules > @material-ui > core > esm. If you are doing it "wrong", you will have the same module both in root of core folder, and in core/esm folder. (see first screenshot)
If you are doing it right, you only have modules in the esm folder. (see second screenshot)

Screenshot_07-15 20 28 54
Screenshot_07-15 20 29 35

@JReinhold Thanks for sharing your experience. We are using a custom eslint rule on the repository: https://github.com/mui-org/material-ui/blob/master/packages/eslint-plugin-material-ui/src/rules/restricted-path-imports.js for our own use cases.

I would personally encourage people to import from the barrel index:

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

We are using a custom eslint rule on the repository: https://github.com/mui-org/material-ui/blob/master/packages/eslint-plugin-material-ui/src/rules/restricted-path-imports.js for our own use cases.

Oh wow, that's cool, is it possible that it can get published? I can see that it is on the registry, however that published version is 3 years old and doesn't contain that rule.

I would personally encourage people to import from the barrel index:

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

Yeah, we've restrained from doing that specifically, because in our experience, it slows down the TypeScript compiler a considerable amount. However I've just tested with a few components, and that doesn't seem to be the case anymore (maybe something has changed in v4?), so that's probably a good option.

Oh wow, that's cool, is it possible that it can get published?

@JReinhold The eslint rule was written by @eps1lon. I have no objection to publishing it. If the other maintainers agree, we can go for it.

However I've just tested with a few components, and that doesn't seem to be the case anymore (maybe something has changed in v4?), so that's probably a good option.

I'm happy to hear that the performance overhead is no longer present! With #16192, you might be able to automate the migration, at least, to try it.

  1. run npm ls @material-ui/styles command to check if there is any duplicate material-ui in your code.
  2. if any of your package's using material-ui then make sure material-ui package is in devDependency and peerDependency.
    example:
    "peerDependencies": {
    "@material-ui/core": ">=4.7"
    },
    "devDependencies": {
    "@material-ui/core": "4.7.2"
    },

The issue also exists in my development environment when I use npm link. For example:

We have two projects A and B
A depends on @material-ui/core
A depends on B
B depends on @material-ui/core
They both have installed node_modules

I get used to link B to A for debugging. When I run the dev-server on project A, B will look for the module from the nearest path which is installed in directory of B:

/A/node_modules/
    |-- @material-ui/core/...
    |-- B/node_modules/@material-ui/core/...

Therefore, the <style> tags are added twice from the two @material-ui/core modules. I have to do following things with the issue:

  • When I develop A with the linked B, I run mv node_modules/@material-ui node_modules/@material-ui_bak
  • When I develop B separately锛孖 move them back

Hope for any better solution, thank you.

@brandonccx https://github.com/mui-org/material-ui/issues/15610#issuecomment-592485700 is a great solution.

Hi guys,
although this issue is closed, just to confirm that applying some import refactoring and build configuration in webpack/babel, fixed the problem of multi time the same classes to be applied
On top of that, as far as I'm concerned,it fixed also the issue that the global theme defined in the APP is well applied to some components I have created in a separate library I'm importing in the main APP.

It turned out that I had to change the way to build my react/material-Ui components library @mylibrairy/reactcomponentscommon.

1- Make sure that in the library, all imports where such as import { Button} from "@material-ui/core" and not for example import Button from "@material-ui/core/Button"

2- Remove the usage offile-loader pluginin the .babelrc to make sure it doesn't change the way to import material-ui components

3- Push @material-ui/core and @material-ui/icons as dev and peer dependencies in the package.json of the library.

4- Rebuilt the library using webpack and babel to compile typescript tsx to js.

Then, importing the library in the main APP, creating the theme and applying it at the root of the App worked like a charm, whereas I had stronge behavior from one component to another before applying this method.

However to be fair, I do not really understand the reason why this refactoring and the move from dependency to dev and peer fixed the problem.

It's closed, but just my two cents:
My package had @material-ui/core/esm in the node_modules of the built code, which caused the duplication issue. I couldn't make my package use MUI as a peerDependency, so adding this to my Webpack config solved it for me.

resolve: {
  alias: {
    '@material-ui/core': path.resolve(__dirname, 'node_modules', '@material-ui/core')
  }
}

We're running into the same issue, but in our case with components that come from 'lab':

Neither of the following seem to work:

import ToggleButton from '@material-ui/lab/ToggleButton';
import ToggleButtonGroup from '@material-ui/lab/ToggleButtonGroup';
// import { ToggleButtonGroup, ToggleButton } from '@material-ui/lab';

There's always duplicate MuiBaseButton style definitions. Any thoughts? 馃檹 @oliviertassinari maybe?

EDIT: w/ the following versioning btw

鈹溾攢 @material-ui/[email protected]
鈹溾攢 @material-ui/[email protected]
鈹溾攢 @material-ui/[email protected]

I also have a duplicate MuiButtonBase definition.

I'm pretty sure the problem comes from two bundles that are included on the same page as mentioned here.

Since the CommonsChunkPlugin isn't there anymore I tried to replicate the example with the SplitChunksPlugin.
Here is the part I added to my webpack.config.js

optimization: {
    splitChunks: {
        cacheGroups: {
            vendor: {
                test: /[\\/]node_modules[\\/](@material-ui\/styles)[\\/]/,
                name: 'vendor',
                chunks: 'all',
            },
        },
    },
},

Then I included the vendor.js bundle before the two application bundles.

This did not fix the issue. Is there anything I need to change with this config?

As far as I can tell (via the Chrome DevTools > Sources page and by looking through the code) I don't have any wrong imports.
Screenshot 2020-08-20 at 08 01 09

My use case is similar to @jeromeSH26 - creating a library of common components to be used in multiple projects within an organization. His advice is good, but in order to get this fully working for my setup, I also had to declare all material UI components as externals in the webpack config for my library, and I did this with a regex like so:

  externals: [
    {
      react: 'commonjs react',
      'react-dom': 'commonjs react-dom',
    },
    /@material-ui\/.*/
  ]

Everything is now working as expected. Hope this helps someone.

@nkpz does using webpack externals require including a script tag (in html where bundles and entry point is )with src being a CDN url?

@avelosa No, defining an external will just tell it not to bundle the dependency and to look for it elsewhere in the bundle at runtime. It's useful for authoring a component library as an addon to mui because everything using your library will already have its own version of react, react-dom, material-ui etc

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sys13 picture sys13  路  3Comments

rbozan picture rbozan  路  3Comments

ryanflorence picture ryanflorence  路  3Comments

ghost picture ghost  路  3Comments

newoga picture newoga  路  3Comments