material-ui
material-ui copied to clipboard
[base] Make CSS class prefixes consistent
Make the Base unstyled components' CSS classes follow the same pattern: BaseComponentName
.
This change doesn't affect SliderUnstyled and ModalUnstyled as they are used in their respective Material UI components. We don't have the possibility to control the class name prefix per component yet. Once we implement this feature, we can also rename these two prefixes.
This is a breaking change for anyone who depends on the class names applied to Base components.
If you use the <component>UnstyledClasses
objects, you won't notice a difference. Only if you depend on the resulting class names (e.g. in CSS stylesheets), you'll have to adjust your code.
Closes #33260
https://deploy-preview-33411--material-ui.netlify.app/material-ui/react-button/
@material-ui/core: parsed: +0.07% , gzip: +0.12% @mui/material-next: parsed: -38.69% :heart_eyes:, gzip: -38.43% :heart_eyes:
Generated by :no_entry_sign: dangerJS against 61babab2576a1aaa74466c04d89a08e73381bb44
This change doesn't affect SliderUnstyled and ModalUnstyled as they are used in their respective Material UI components.
This is what caught my attention to have a closer look. This sounds like we solve an inconsistency state issue by taking a path that doesn't minimize breaking changes to reach the consistent state 😁.
We don't have the possibility to control the class name prefix per component yet.
Do we have a GitHub issue about this one? I can think of one possible use case (I miss the context to think of others): for a company that wants to change Mui
to their own company acronym? But maybe we shouldn't allow it? Maybe it would help us to not allow it, so that when developers open their dev tools to look at how "company name" implement the UI, they see our logic. Kind of like when on https://dynaboard.com/pricing, you see Radix 😁

@oliviertassinari
taking a path that doesn't minimize breaking changes to reach the consistent state
Could you explain what you mean by that? We don't introduce breaking changes in stable packages (nor do we intend to do so). From a point of view of Material UI users, nothing should change.
I can think of one possible use case
It could be used to avoid conflicts when using a library that uses MUI products internally (see #30925) or when styling different parts of the application differently (#28550).
Also, it could be helpful to solve this very issue - to rename Base classes and add Mui-
prefix to them.
Do we have a GitHub issue about this one?
I don't think so, I haven't created one myself yet.
Could you explain what you mean by that?
I understand #33260 as about solving the inconsistency in the convention used inside MUI Base.
What seems to solve the inconsistency while minimizing the number of breaking changes seems to be about doing the change that I have suggested. I feel that in this PR, we trying to kill two birds with one stone, but the second bird (unstyled to base) doesn't seem to have its issue to explain the rationale of the breaking change, and is done in a way that leads developers to have an inconsistent experience. I would be in favor of:
- Killing one bird at a time when possible (different PRs)
- Describing the second bird in an issue so that the community/maintainers in the future can look back at it to understand the rationale
- Try to keep the product as consistent as possible at any given point in time. MUI Base is unstable, so we can do BCs, so it seems that we could replace everything at once (class names, components, exports, docs) from "Unstyled" to "Base".
It could be used to avoid conflicts when using a library that uses MUI products internally
With Material UI v4 I assume. I believe the problem is solved with Material UI v5 with the us of emotion, class names with styles are no longer global.
With MUI Base, I guess the problem will happen, but how would we change the class names of only the components we care about?
I've changed the approach based on the results of the poll.
Snapshot:
All components, regardless of the package they belong to, will use the .MuiComponentName-slotName
pattern. So .MuiButton-root
, .MuiSwitch-track
, and so on. Additionally, the root slot will have the class specific to a product. Currently, I settled on .Mui-Base
and .Mui-MaterialUI
.
Technically this is done by each package providing its own generateUtilityClass
function. Previously, the one from Base was used in both Base and Material UI. This PR changes the implementation to treat the function from @mui/utils
as the foundation. MUI Base and Material UI import the utils version and modify its logic.
The generateUtilityClass
and generateUtilityClasses
are not exported anymore from @mui/base index. They are still accessible via a subdirectory import, but we should treat them as internal.
Currently, I settled on .Mui-Base and .Mui-MaterialUI.
@michaldudak what did you settle on for the last product? Based on the previous names I would assume .Mui-JoyUI.
I like how it matches what we teach developers with .Mui-focused
.
It makes me think that if we wanted to optimize for the developers that write raw CSS (same hand muscles used as writing, e.g. font-size
), we would have probably gone with mui-focused
, .mui-base
, .material-ui
, and .joy-ui
but we never had this use case in mind anyway.
A side note, we will force developers to increase CSS specificity to target Material UI only, which would mean that they will override the pseudo-states when they apply a .MuiButton-root.Mui-MaterialUI
class name. So in practice, they will have to restyle all the different states. So I conclude that by going with the last option, we are in practice deciding: that developers shouldn't apply CSS with global class names with multiple MUI products. If they do, they will really struggle to customize all the states of the components. 👍
Yes, I think .Mui-JoyUI makes sense (cc @siriwatknp).
It makes me think that if we wanted to optimize for the developers that write raw CSS (same hand muscles used as writing, e.g. font-size), we would have probably gone with mui-focused, .mui-base, .material-ui, and .joy-ui but we never had this use case in mind anyway.
I created a POC of a utility that lets developers customize how classes are generated - this could help in this use case: #34380
that developers shouldn't apply CSS with global class names with multiple MUI products
It's still possible with the help of the :where()
CSS pseudo-class that doesn't increase specificity:
.MuiButton-root:where(.Mui-MaterialUI) { ... }
Yes, I think .Mui-JoyUI makes sense
Order by what I like the most:
-
.Mui-joy .Mui-material
-
.joy-ui .material-ui
-
.Mui-JoyUI .Mui-MaterialUI
(theui
andUI
looks strange and too long + shift to type)
@michaldudak I didn't know about :where()
, it sounds great and enough.
@siriwatknp Option 1, sounds good too.
@siriwatknp the second option looks the best to me, but it would be inconsistent with what we already have. The first one is OK as well. I'll update the PR.
I noticed that in the button, we have the same class twice, I guess it comes from both the ButtonBase and the Button component.
Same as Autocomplete and other components that have nested components. I wonder if this should be fixed in this PR or a follow-up one.

Good catch. I'll see what I can do about it. You're right that it comes from components that use nested components in their roots (like Button using ButtonBase).
I noticed that in the button, we have the same class twice, I guess it comes from both the ButtonBase and the Button component. image
I propose we leave it as is for now and see if people complain. Otherwise we may hurt perf for removing classes from string on each render.
see if people complain.
I think that it could be even better to start without the .Mui-Material
classes and equivalent, see if developers raise bugs that these could solve for them, if they don't raise real problems, we get the best of our of the two worlds.
When you inspect the DOM nodes to find what I need to customize, it adds more "weight":

As far as I understand it, the people that these classes will benefit are people who don't follow best practices. We might not want to degrade the experience for the people who do.
see if people complain.
I think that it could be even better to start without the
.Mui-Material
classes and equivalent, see if developers raise bugs that these could solve for them, if they don't raise real problems, we get the best of our of the two worlds.When you inspect the DOM nodes to find what I need to customize, it adds more "weight":
![]()
As far as I understand it, the people that these classes will benefit are people who don't follow best practices. We might not want to degrade the experience for the people who do.
Make sense.
I think that it could be even better to start without the .Mui-Material classes and equivalent
@oliviertassinari according to the poll results, if I understand you correctly, what you're proposing is the third most popular option with just 14% votes. Doesn't it defeat the purpose of the poll?
@oliviertassinari, @siriwatknp, there's another option we can consider - we don't add the product classes (.Mui-base
, .Mui-material
, etc.) here. Still, we allow developers to add them if needed (with #34380, after some additional work). This way, we don't add classes unnecessarily but allow customization for those who need them. What do you think?
@oliviertassinari, @siriwatknp, there's another option we can consider - we don't add the product classes (
.Mui-base
,.Mui-material
, etc.) here. Still, we allow developers to add them if needed (with #34380, after some additional work). This way, we don't add classes unnecessarily but allow customization for those who need them. What do you think?
That sounds good to me as well. I like the opt-in version so that the classes are included when they are needed.
what you're proposing is the third most popular option with just 14% votes.
@michaldudak Correct, I'm challenging whether we are solving an existing problem with an extra class name to be able to differentiate the different default CSS/HTML structures.
we allow developers to add them if needed
So the problem considered would be: I have Material UI in my app, and now I would like to migrate to Joy UI. However, because I have done global CSS customizations on Material UI. So when I add the first Joy UI components the styles conflict. I guess we have a limited set of options to solve this:
- We tell developers, first migrate all your customization to be more narrow. It's not ideal because developers have to do upfront wrong.
- I assume that the path of having a global CSS customization would only work if each product has its own React context. I suspect this would be a very heavy solution from a bundle size perspective, maybe out of dimension compared to 1. It would also be far from obvious for developers to discover that this customization is possible.
- Have each product with a different class name by default, option 3 of the poll https://github.com/mui/material-ui/pull/33411#issuecomment-1249280951
- Have an extra class name on the components.
From my perspective, the problem considered is one we shouldn't optimize for, so I would vote for the options in this order: 1 > 3 > 4 > 2
We discussed this with @oliviertassinari in a meeting. We agreed to use this PR just to make the classes in Base consistent (.MuiComponentName
), not to add product classes here, and work on userland customization with a lower priority in #34380.
The PR has been updated to include the latest agreements. @oliviertassinari, @siriwatknp, it's ready to take another look on, if you'd like.
Regarding Joy UI's class name, I guess it's outside of the scope of this PR?
Yep, this change is only about Base.
Let's discuss the usage of @mui/utils and whether we should import from Utils or Base or any other package separately. I'm going to merge this PR since there are no other comments related to the class names.