Material-ui: [RFC] v5 styling solution ๐Ÿ’…

Created on 24 Aug 2020  ยท  105Comments  ยท  Source: mui-org/material-ui

This RFC is a proposal for changing the styling solution of Material-UI in v5.

TL:DR; the core team proposes we go with emotion

What's the problem?

  • Maintaining & developing a great styling engine takes a considerable amount of time. We have experienced it first hand. Over the last 12 months, we have preferred to invest time on our core value proposition: the UI components, rather than improve the style engine. Working on it has a high opportunity cost.
  • We have been facing issues with supporting dynamic styles for the components. The performance of our custom dynamic styles implementation (based on props) isn't great (see the performance benchmarks below). This is seriously limiting the quality of the Developer Experience we can provide. It's a blocker for improving our API around customizability or ease of writing styles. For instance, it will unlock: style utils props, color variant, and custom variant.
  • The React community, at large, hasn't voted for using JSS at scale (JSS is great and still used). 3 years ago we bet on the best option available. We have to recognize better options are available now. We can move faster and unlock better DX/UX by building on top of a more popular, existing, styling solution.
  • Many developers use styled-components to override Material-UI's styles. End-users find themselves with two CSS-in-JS libraries in their bundle. Not great. It would be better if we could offer different adapters for different CSS-in-JS libraries. (Potential problems: we may need to re-write the core styles to match the syntax of the engine used ๐Ÿคทโ€โ™€๏ธ)

What are the requirements?

Whatever styling engine we choose to go with we have to consider the following factors:

  • performance: the faster the better but we are willing to trade some performance to improve the DX.
  • bundle size: below our current 14.3 kB gzipped would be great.
  • support concurrent mode: @material-ui/styles has partial support as I'm writing.
  • support SSR
  • simple customization
  • allow dynamic styling
  • good community size
  • theming
  • flat specificity
  • RTL
  • TypeScript

It would be nice if it can support the following:

What are our options?

  • styled-components
  • emotion
  • JSS (currently wrapped in material-ui)
  • styletron
  • Aphrodite
  • fela
  • else?

Comparison

Performance

Here are benchmarks with dynamic styles of several popular libraries (note the Material-UI v4 only use static styles which have good performance):

PR for reference: https://github.com/mnajdova/react-native-web/pull/1

Based on the performance, I think that we should eliminate: JSS (currently wrapped in @material-ui/styles), styletron, and fela. That would leave us with:

  • styled-components
  • emotion
  • Aphrodite
  • JSS
  • react-styletron
  • fela

Dynamic props

Based on the open issues, it seems that Aphrodite doesn't support dynamic props: https://github.com/Khan/aphrodite/issues/141
which in my opinion means that we should drop that one from our options too, leaving us with:

  • styled-components
  • emotion
  • Aphrodite
  • react-styletron

npm

While styled-components and emotion are both libraries are pretty popular, react-styletron at the time or writing is much behind with around 12500 downloads per week (this in my opinion is a strong reason why we should eliminate it, as if we decide to go with it, the community will again need to have two different styling engine in their apps).

Here is the list rang by the number of Weekly downloads at the time of writing:



Note that storybook has a dependency on emotion. It significantly skews the stats.

  • styled-components
  • emotion
  • react-styletron

Support concurrent mode

  • emotion: YES. Since v10 it is strict mode compatible based on their announcement post. I have tested it on a simple project that works as expected.
  • styled-components: Partial. There is at least one bug with global styles in strict mode.

SSR

Stars

  • styled-components: 30.6k
  • emotion: 11.4k
  • JSS: 5.9k

Trafic on the documentation

SimilarWeb estimated sessions/month:

  • sass-lang.com: ~476K/month (for comparison)
  • styled-components.com: ~239K/month
  • emotion.sh: ~59K/month
  • cssinjs.org: <30k/month (for comparison)

Users feedback

Based on the survey, 53.8% percent are using the Material-UI styles (JSS), which is not a surprise as it is the engine coming from Material-UI. However, we can see that 20.4% percent are already using styled-components, which is a big number considering that we don't have direct support for it. Emotion is used by around 1.9% percent of the developers currently based on the survey.

Having these numbers we want to push with better support for styled-components, so this is something we should consider.

Browser support

  • emotion: modern evergreen browsers + IE11
  • styled-components: not documented for v5, but the previous versions support the following

Bundle size

What's the best option?

Default engine

Even if we decide to support multiple engines, we would still need to advocate for one by default and have one documented in the demos.

styled-components

Pros:

  • Has the biggest community, people love to use it.
  • Performance starting from v5 is good.

Cons:

  • It will mean that all components styles need to be created using the styled API, which means for developers they will always have wrapper components if they need to re-style.
  • Lack of full concurrent support, which may create blockers down the road.

emotion

Pros:

  • Relatively large community, growing.
  • Good performance.
  • Concurrent mode + SSR would be possible out of the box.
  • The CSS prop can be useful for overrides.
  • Source map support.
  • A bit smaller.

Cons:

Support multiple

We may try to support multiple CSS-in-JS solutions, by providing our in house adapters for them. Some things that we need to consider is that, that we may have duplicate work on the styles, as the syntax is different between them (at least jss compared to styled-components/emotion). We will reuse the theme object no matter what solution we will pick up.

The less involved support for this may come from the usage of the styled, as people may do some webpack config to decide which one to use - (this is just something to consider).

Additional comments

Deterministic classnames on the components that can be targeted for custom styles

Regarding how the classes look and how developers may target them, I want to show a comparison of what we currently have and how the problem can be solved with the new approach.

As an example, I will take the Slider component. Here is currently how the generated DOM look like:

Each of the classes has a very well descriptive semantic and people can use these classes for overriding the styles of the component.

On the other hand, emotion, styled-components or any other similar library will create some hash as a class name. For us to solve this and offer the developers the same functionality for targeting classes, each of the components will add classes that can be targeted by the developers based on the props.

This would mean that apart from the classes generated by emotion, each component will still have the classes that we had previously, like MuiSlider-root & MuiSlider-colorPrimary, the only difference would be that this classes will now be used purely as selectors, rather than defining the styles for the components. This could be implemented like a hook - useSliderClasses

Conclusion

No matter which solution we would choose, we would use the styled API, which is supported by the two of them. This will allow us down the road to have easier support for styled + unstyled components (probably with webpack aliases, like for using preact).

After we investigated the two options we had in the end, the core team proposes we go with emotion. Some key elements:

A small migration cost between styled-components and emotion

Developers already using styled-components should be able to use emotion with almost no effort.

There are different ways for adding overrides other than wrapper components

The support of cx + css from emotion can be beneficial for developers to use it as an alternative for adding style overrides if they don't want to create wrapper components.

Concurrent mode is for sure supported :+1:

Kudos to @ryancogswell for doing a deeper investigation on this topic. So far we did not find anything in @emotion's code that would give us concern that concurrent mode wouldn't work.
We were also looking into createGlobalStyle from styled-components as a comparison to emotion's Global component. It is doing most of its work during render (inherently problematic for Strict/Concurrent Mode) and just using useEffect for removing styles in its cleanup function. createGlobalStyle needs a complete rewrite before it will be usable in concurrent mode -- it isn't OK for it to add styles during render if that render is never committed. It looks like someone has tried rewriting it with some further changes in the last month, so we will need to follow this progress.

How is the specificity handled

Emotion's docs recommend doing composition of CSS into a single class rather than trying to leverage styles from multiple class names. In v5, our existing global class names would be applied without any styles attached to them. The composition of emotion-styled components automatically combines the styles into a single class. This potentially gets rid of these stylesheet order issues at least internal to the styles defined by Material-UI, because every component's styles are driven by a single class name :+1:.
So we would have the global class names (for developers to target in various ways for customizations) and then a single generated (by emotion) class name per element that would consolidate all the CSS sources flowing into it.
Specificity is then handled by emotion based on the order of composition.
All compositions using emotion (whether render-time or definition-time composition) results in a single class on the element.
styled-components does NOT work this way concerning render-time composition (definition-time composition does get combined into a single class). The same composition in styled-components results in multiple classes applied to the same element and the specificity does not work as I would have intended.

Alternatives


What do you think about it?

discussion

Most helpful comment

Speaking as Emotion's maintainer - this sounds great ๐Ÿš€

We should also be able to release a new major soon-ish which won't be any revolution, just some cleanups, overall improvements, hooks under the hood and TS types improvements (better inference and performance).

Oh, and rewritten parser! that eliminates some edge bugs in Emotion and Styled-Components (as currently, they are both using the same parser). It is both smaller and faster.

All 105 comments

Just to a couple of corrections:

The React community, at large, has voted against using JSS at scale.

I would suggest instead that the React community didn't vote _for_ JSS. Perhaps it wasn't "marketed" as well as other solutions?

We didn't bet on the right horse.

We bet on the only horse โ€“ it was a one-horse race. None of the other solutions available at the time ticked all the boxes.

Emotion sounds great! I love getting TS support, e.g., autocomplete and all the benefits of typing - with CSS-in-JS - when building UI or styling components, will that still be possible? How is this going to affect that, if at all? TS support is apparently better, amazing!

The last bit got me! I'd love helping by doing this behind a beta flag or develop on some features:

All compositions using emotion (whether render-time or definition-time composition) results in a single class on the element.
Styled-components do NOT work this way concerning render-time composition (definition-time composition does get combined into a single class).

I also noted that the theme object is going to stay the same, the - in my opinion - the absolute best way to theme an application! I have nothing else to say :astonished:

Thanks for the great work on M-UI. I love the direction the project is going.

Moving to a more standardized way of styling is the way to go. I know the team and community will work out the kinks, and v5 - by the sounds of it - is going to be even more awesome! :rocket:

As someone who has used both styled-components and emotion I can confirm transitioning between them is easy and painless.

+ emotion is more typescript friendly

Speaking as Emotion's maintainer - this sounds great ๐Ÿš€

We should also be able to release a new major soon-ish which won't be any revolution, just some cleanups, overall improvements, hooks under the hood and TS types improvements (better inference and performance).

Oh, and rewritten parser! that eliminates some edge bugs in Emotion and Styled-Components (as currently, they are both using the same parser). It is both smaller and faster.

What about breaking changes, which option introduce more breaking changes (if any)?

Not sure if it makes a difference but were the benchmarks done with emotion and/or style-component's babel plugins? They help to optimize things further.

It was my understanding that MUI had previously indicated it was going with styled so this is a surprise. I think emotion is a great library, but with more people using styled currently it's important you find ways to give them good options if they don't want to migrate to emotion

@ee0pdt Emotion is very, very much like styled. I think that's part of the whole choice for Emotion over styled-components. There are clear benefits, and migration debt is little to none.

And there's a whole section about supporting both by giving a choice to developers. That could be a way to go, but then again, standardizing would probably help future us more. The full concurrency and no wrapper components is a deal-breaker for me. I get that others might want something styled provides, and that should be considered. I'd rather push toward standardization

Why was styletron-react ruled out? It leaves out a whole metric that wasn't considered, which is memory consumption. The default styletron engine (and fela) is atomic. Whilst a bit slower it saves a lot of memory. Having seen a lot of react pages do nothing and go >1GB after a while it's a bit of a concern. The browser freezes after that.

With an atomic framework performance improves over time globally, across components as each atomic "class" is cached. Likely not reflected in the test either.

Happy with either as long as it supports SSR

I just withdrew my vote from the original styled components issue :sweat_smile: - first learned to know emotion through storybook, but It will mean that all components styles need to be created using the styled API, which means for developers they will always have wrapper components if they need to re-style. got me to switch.

Thanks everyone for the quick feedback. Here are answers to some of the comments/questions.

What about breaking changes, which option introduce more breaking changes (if any)?

@sag1v using styled-components vs emotion does not introduce any more or less breaking changes that we will need to handle. The overall breaking changes would be around how the overrides inside the theme would look like:

// previosly
root: {
  contained: {
    '&$disabled': { // <-- this part will need to be transformed
      color: 'red',
    },
  },
  containedPrimary: {
    color: 'blue',
  },
}

// after
root: {
  contained: {
     '&.Mui-disabled': {
      color: 'red',
    },
  },
}

However as the styles syntax between emotion & styled-components is identical, it won't make any difference.

Not sure if it makes a difference but were the benchmarks done with emotion and/or style-component's babel plugins? They help to optimize things further.

@hc-codersatlas nope, but the perfs are those are anyway between the top few, so I don't believe it would make any difference. Good call tough!

Why was styletron-react ruled out? It leaves out a whole metric that wasn't considered, which is memory consumption. The default styletron engine (and fela) is atomic. Whilst a bit slower it saves a lot of memory. Having seen a lot of react pages do nothing and go >1GB after a while it's a bit of a concern. The browser freezes after that.

With an atomic framework performance improves over time globally, across components as each atomic "class" is cached. Likely not reflected in the test either.

My comments around why styletron-react was ruled out may be a bit misleading sorry about that, will update the PR description rightaway. The perf are good, but the biggest concern I have with styletron is the community: https://www.npmtrends.com/styletron-react-vs-@emotion/core-vs-styled-components While both emotion and styled-components are over 2000000 downloads in the last 6 months, styletron is around 15000.

Also atomic css may cause issues with overrides, as each classname contains only one styler rule.

I have got a question if we decide using emotion we want to add below code on top of all files?
/** @jsx jsx */
This is JSX pragma, by default JSX pragma is React.createElement

You need to add it if you are using the css property in emotion. For the styled API as well as the regular className API you don't need it.

It is possible to skip adding JSX pragma, but it requires extra Babel setup and it comes with worse support from the tooling - for example, TS is not able to type-check your CSS prop as accurately as it does when using JSX pragma. More information can be found here: https://github.com/emotion-js/emotion/pull/1941/files#diff-9abe25e5d2b00958d4b9849f5f20c139R5

@mnajdova thanks. I was just hoping memory usage is covered more than vouching for styletron in particular. As to downloads or community - "only" Uber uses styletron :) so no worries. Voted for emotion in the first place.

Would be cool if there was a babel plugin or similar that can transform static styles to real css classes. There's already a similar library called compiled. Most styles realistically are static.

To be useful Fela will require a set of plugins like fela-plugin-rtl, fela-plugin-prefixer which will make performance even worse (https://github.com/microsoft/fluentui/pull/12289) ๐Ÿข And then you will probably end up with Emotion (https://github.com/microsoft/fluentui/pull/13547) as sometimes it can be twice faster than Fela ๐Ÿ“ฆ

My only concern is having to use css thingy from Emotion. That is a big red-flag based on my experience. I had to remove such thing from a large codebase and wasn't fun. Why? Because it is an abstraction that brings more problems than the one that solves in the long term.

Most of the time, we try to use styled function, but we are quite happy with makeStyles function for generating some classes in some cases.

Hopefully, there is no breaking change for those two APIs, and the I am not forced to use css macro.

My only concern is having to use css thingy from Emotion.

@yordis You definitely won't be forced to use the css prop. I suspect there will be some degree of change for usages of makeStyles and withStyles. Hopefully the amount of change necessary can be mostly limited to a codemod on the imports. I don't think the approach used in makeStyles or withStyles will be practical to support using emotion, so I would expect any ongoing support of those APIs to be through a separate package (so that "@material-ui/core" no longer has a JSS dependency) which would probably receive only minimal maintenance after v5 is released (the details around what happens to makeStyles and withStyles aren't nailed down yet, so this is just my speculation about the implications of moving forward with emotion).

๐Ÿ‘ the choice to use Emotion. Avoiding the styled API of styled-components is one reason my team chose Emotion (which also supports a similar styled API in addition to the css prop). We presently use Emotion for Material UI style customization and it works pretty well. Built in support would just be icing on the cake.

Regarding these facts:

Many developers use styled-components to override Material-UI's styles. End-users find themselves with two CSS-in-JS libraries in their bundle

Support concurrent mode
styled-components: Partial

If styled-components had full support for concurrent mode, would it be a more sensible choice, given most developers are overriding MUI with styled-components (excluding JSS)? The point about emotion being smaller in bundle size is moot if 2 css-in-js solutions need to be included. And I would presume most practical applications of MUI involve overriding its styles.

Where I and my team use Emotion as a main way of styling components, I came across some inefficiencies present in emotion library. And I wonder what you guys think about these inefficiencies explained below.

Consider below Emotion StyledComponent;

const StyledComponent = styled.div`
  ${({color}) => color && `color: ${color}`};
  display: flex;
  justify-content: center;
  align-items: center;
  background: teal;
  font-size: 20px;
  padding-top: 8px;
  padding-bottom: 8px;
  margin-top: 12px;
  margin-bottom: 12px;
  border: 1px solid grey;
`

When color prop changes new css class is generated with all css props (display: flex, justify-content, ..., border: 1px soild grey) copied over. Which would result in css classes with the exact same css props for each color prop, see below;
image

Another inefficiency we have found is when a new component is derived from above StyledComponent it will create a new class with all the css props copied over from base StyledComponent. Consider below;

const DerivedComponent = styled(StyledComponent)`
  font-family: monospace;
`

It creates another css class that only adds font-family: monospace to the above css class generated from StyledComponent. That is, It creates a css that has all the props copied over from StyledComponent as can be seen below;

image

Now if above StyledComponent and DerivedComponent are used together we (initially) have two css classes which have duplicate css props, (differing only in font-family). As can be seen below;

image

As you can imagine a number of css classes that have duplicate css props of each other can grow quite quickly.

I found that with Emotion, as component styles are composed together, we end up with css classes with many duplicate css props.

I am not sure this duplicate of css props in each css class has any noticeable impact on apps, but I wonder if this is taken into account on choosing to use emotion.

I am not questioning the performance of Emotion in creating and applying CSSStyle at run time. Emotion is one of the fastest on applying CSSstyles as evident in perf tests.
I am just concerned resulting CSSstyle is bloated. And as Emotion can be SSR(ed) which means styles are inlined in HTML, we just might get unnecessarily bloated (with css style tags) HTML file. Which in turn results in lot more tags to parse with unnecessary css props by browsers.

If styled-components had full support for concurrent mode, would it be a more sensible choice, given most developers are overriding MUI with styled-components (excluding JSS)? The point about emotion being smaller in bundle size is moot if 2 css-in-js solutions need to be included. And I would presume most practical applications of MUI involve overriding its styles.

@petermikitsh the reasons why we concluded on emotion are actually the four points in the conclusion

  • A small migration cost between styled-components and emotion
    Developers already using styled-components should be able to use emotion with almost no effort.
  • There are different ways for adding overrides other than wrapper components
    The support of cx + css from emotion can be beneficial for developers to use it as an alternative for adding style overrides if they don't want to create wrapper components.
  • Concurrent mode is for sure supported ๐Ÿ‘
  • How the specificity is handled

Having the first point in mind, if developers really want to avoid having two css-in-js solutions in the bundle, the migration cost is really small for moving to emotion + it supports different API other than the styled.

@ko-toss thanks for writing this up these are all good points. The fact that emotion generates one className with all styles, makes it the better engine for resolving overrides. The problem we may have with generated multiple classNames is that the last class written will win, which may become problematic in the future.

On another project, I was using atomic css, where from memory consumption is much better because all css rules are written only once, but doing predictable overrides there is pretty hard, as each className is one atomic rule, and than again which one wins, depends on the order of which they are written, if you do not decide to previously process all styles and merge them correctly, which may impact perf in the end.

On the other hand, I believe that using any css-in-js solution, people will not just randomly create infinite combination of styles, they are still pretty structured based on the props value.

However, again these are good points, that we will have in mind, thanks a lot for sharing them ๐Ÿ‘

  • else?

what about [stylex] idea compatibility (like [style9])

  • else?

style9 or any other CSS engine that is compatible with stylex idea

This seems to require a build time setup which would discourage many people from using it, especially beginners.

(this is rather FYI, emotion is a good choice)

https://github.com/cristianbote/goober (1kB, perf little worse than emotion)

I have no experience with that yet but I want to try it one day.
image
image

@cztomsik Similar to https://github.com/kuldeepkeshwar/filbert-js but has no JavaScript syntax support (only CSS template string)

Here are some tests done with google lighthouse on time-to-interaction time:

https://jantimon.github.io/css-framework-performance/

css-lighthouse-scores

FYI, I've done some detailed benchmark of styled-components v5, emotion v10, and emotion v11, with/without babel plugin, with vanilla API, css props API, and styled API. Hope this help the discussion!

https://github.com/simnalamburt/css-in-js-benchmark

Did you consider the new wave of css-in-js libraries that rely heavily on atomic css and typescript support?

Did you consider the new wave of css-in-js libraries that rely heavily on atomic css and typescript support?

It's not on the benchmark, but currently otion is 2~4 times slower than emotion. I think otion indeed have pretty big potential and believe there is a room for the optimization, but otion is not really ready for the production yet.

I didn't test the stitches yet, though. ๐Ÿ˜ƒ

What about an actual zero-runtime library? I haven't seen anyone mention linaria.

I stumbled across linaria at some point and I really like it. My only worry with is that the dynamics props styles solely depend on css variables, and there isn't any support for IE 11 based on https://github.com/callstack/linaria/issues/445 Also compared to styled-components and emotion the community is much smaller at this moment.

@TheHolyWaffle
Linaria is awesome.
Iff you set it up properly, I believe it is the best of both css-in-js (in terms of dev experience), and pure css (can't beat pure css performance). It even optimizes(dedupes) and reuses css rules.
But linaria requires build and bundling step which would be hard for beginners.

I would love to see ports for other css-in-js libraries with similar API surface e.g filbert-js /goober

@kuldeepkeshwar We will let you know once we look into the adapters for the styled API :)

How does https://compiledcssinjs.com/ fit into all of this? It seems to be an incredibly interesting approach; Compiled also runs RFCs for the project, which proves to be great for open source and collaboration. _wink wink_

I think the future is very, very bright for styling the web, and I hope Material-UI will be an integral part of the go-to solution for styling any app.

The explanation of how Compiled works got to me:

This kind of transformation allows us to deliver your component to any consumer without needing them to configure/setup their tooling. Just import and go. This is powerful, and more importantly, the same as current CSS in JS libraries work - with one catch.

_CSS can't be generated at runtime._

This single constraint opens a lot of doors for us. Build time optimizations. Runtime guarantees. Performance bottlenecks gone.

On a different side note, I'd like to point out that popularity doesn't mean much for a good project. I love MUI and the work that has gone into it thus far; I also think it is fantastic it has become a premium product.
But choosing a 'popular' _name_ for the sake of popularity isn't a reasonable argument.
I've seen popularity referenced multiple times, and I very much dislike even considering if x amount of people use x technology - MUI is (in my books) focused on performance, DX, and other things, just not popularity.

MUI didn't always have 60k stars, it got them because it chose the best tech (or close to), not because it chose popular tech.

If choosing based on popular vote is in regards to being a more widely approachable project, that's business concerns, not possible performance enhancements. A project lives with or without users. It dies with bad choices. I think there's so many sayings around this and reading "it isn't popular enough, therefore it is a bad choice" rings a lot of loud bells.

People use a right product because it is good, not because it uses popular technology; MUI was niche once but became famous even though it had CSS-in-JS, which didn't win the popular vote btw. It just has some amazing properties and made the right choices that weren't based in current community but the actual DX and performance.

That sidenote being noted, I am on the side of the popular vote myself; so if anything, I am also sabotaging myself. I don't have any personal gain to have from choosing a way less popular product, I have the opinion that popularity should not be considered _at all_ when talking about revolutions and changes. Please reconsider some of these options based on what they actually are, not what people think they are based on the current popularity of the option.

To actually finish off, I am grateful for every thought and any amount of time going into MUI. I have made some amazing (sadly private) solutions following all standards etc. the past couple years, which would have taken months or years to make singlehandedly! I can't describe my appreciation nearly enough for it to shine through on paper ๐Ÿ™‡โ€โ™‚๏ธ ๐Ÿ™ ๐Ÿ™‡โ€โ™‚๏ธ

I'm curious if Compiled even is an option and how it would work with adapters and such. I think the Compiled approach:

Compiled compiles your CSS in JS at build time by statically analyzing your code and then transforming it into Compiled Components. Everything we need to use the component is included along side it in the JavaScript bundle.

is a path to think about, given the whole compiling 'css at runtime' constraint.

I'm saying this as Emotion maintainer - Compiled is great. Or rather - it might be in the future, this is very exciting stuff but it's still quite early in the experimentation. I highly doubt that MUI can go with it at this point in time.

Correct me if I'm wrong but compiled implies having a config which means it would be obligatory to have a config file for MUI even without using any custom styles.

I would hate to be forced to create a config just to use MUI. On a side note: wouldn't that be hard to use in opinionated bootstrappers like Create React App?

@Andarist I agree entirely. I would suggest starting a collaboration or at least considering participating in the development of the library. I'm inquisitive about where it could lead in the future! :eyes: I think something like compiled - as you're saying - in the future is going to be great. It would be awesome to have more great minds get together to make something remarkable.

@shilangyu I am not sure what you are implying, since I might be missing something. So I'll just say that the frontpage of compiled has this to say about it:

Migrate to a zero config reality

The APIs we love are all here for the ride - CSS prop and class names component too! Our consumers don't even need to change how they consume our components, continuing the zero config story they don't need to configure their bundler, nor do they need to setup any specific things for server side rendering. It just works.

Just the beginning

With zero config out-of-the-box today, we're not forgetting what tomorrow could look like. With the possibility for optional CSS extraction, transforming the CSS to an Atomic form, and even being able to use the CSS data for analysis across our code base, we're thinking up an exciting tomorrow.

@MathiasKandelborg
I skimmed through https://compiledcssinjs.com/ . Isn't it still runtime css-in-js ?
It creates css classes at build time but it applies that style(creates style tag with build time generated css classes) with <CC>...</CC> tag at runtime.
If it is as fast as just using pure css then it really is the future(as it uses css variables). Thanks for sharing
I wonder how faster it is compared to Emotion.

What about an actual zero-runtime library? I haven't seen anyone mention linaria.

What we didn't include in the requirements is that any solution has to be zero-config from the perspective of Material-UI consumers. If I understood zero-runtime solutions then they generate some CSS at compile time. Don't I have to setup my bundler to include it properly?

So while zero-runtime is probably the fastest solution it also requires extra attention. Having a zero-config solution that can be configured to run with zero-runtime would be ideal I guess.

So while zero-runtime is probably the fastest solution it also requires extra attention. Having a zero-config solution that can be configured to run with zero-runtime would be ideal I guess.

Cant really say about current state of Compiled but i was talking about it several times with the maintainer and this roughly is the plan - the idea is to keep support for Emotion & Styled Components APIs, so optimizing code written those should just be a matter of changing the imports and including a transform plugin or a webpack loader. It, for sure, wont handle all code that can be possibly written (JS is wild), but it shohld be able to handle sensibly written code. If it wont be able to compile something the. It will simply throw - forcing one to ditch using it or rewriting a particular part of the code to aid static analysis.

So to sum up - if u go with 0config Emotion (or Styled Components) then it should be possible to adapt Compiled as an optional optimization in the future (if the project will manage to deliver what it promises)

@ko-toss I think it compiles into the styled component at build time. At runtime, the styles from the component are then moved to their rightful place.

As they say on the webpage:

Everything we need to use the component is included along side it in the JavaScript bundle.

We take your initial code in all its glory:

import { styled } from '@compiled/css-in-js';

export const ColoredText = styled.span`
  color: #ff5630;
`;

And then transform it into a Compiled Component:

...
...

Which then at runtime will move the styles to the head of the document.

This kind of transformation allows us to deliver your component to any consumer, without needing them to configure/setup their tooling. Just import and go. This is powerful, and more importantly exactly the same as current CSS in JS libraries work - with one catch.

CSS can't be generated at runtime.


Having a zero-config solution that can be configured to run with zero-runtime would be ideal I guess.

I think you hit the nail on the head. It would feel utopian to suddenly just do that dreaming fondly.

There are some ideas to explore and maybe some collaboration to be had. Some of the code and concepts are a bit foreign to me, so I'm not in the position to go in many details. Here are some of the things I'm excited about:

  • Static analysis - this is a big one. Just imagine getting the data of how you're using a styling solution, getting recommendations based on X rule, convention or spec, and be shown where you can optimize
  • Zero config - even though I would prioritize more freedom in favor of laziness (installing a plugin for x bundler)
  • CSS-in-JSS to pure CSS, basically extracting styles at compile time to include them statically, no need for a runtime.

In regards to IE11 support, how are you looking at stats? I'm sure it's a perfectly viable thing to do. Edge is now based on chromium, and most businesses should be making the switch when MS finally stops support for IE when each OS IE11 was installed on reaches the end of their support cycle. It is indeed a long cycle, but I think maintainers also have a part in pushing changes, and keeping support for something that is essentially deprecated seems to enable people to wait until the 'shift' actually happens.

It would be nice to have the option not to support IE11. It isn't industry standard anymore, and will be deprecated. It is a matter of time, and _default_ support from amazing things like MUI probably holds back the shift.

It would be nice to have the option not to support IE11.

See https://github.com/mui-org/material-ui/issues/14420 for that.

We don't plan to drop support for IE completely. The default version likely won't target IE 11 in v5 but we can't choose a solution that won't work at all in IE 11. Or rather it should be a solution that we can swap out at build time and produces the same output.

This makes me happy.

is there a code mod for converting existing jss to styled/emotion I wonder?

Hello everyone. I'm taking the opportunity to shim into this discussion.

In the current version, Material UI makes heavy use of withStyles HOC without any dynamic styles (style being a function that depend on props), which internally uses makeStyles. The performance of makeStyles (without dynamic props) is quite remarkable and Material UI could even be faster if it was using it directly, instead of withStyles, which creates an unnecessary wrapper.

I have created a benchmark forked of this benchmark, and I deployed it to Vercel, so that all code is compiled with production flags on. The benchmark renders cards using different CSS in JSS libraries. Here are the links:

For 100 cards:

For 500 cards:

For 2500 cards:

Overall, emotion and styled-components are very similar in performance. However, makeStyles seems to be 2-4x faster overall on mount and rerender 6-8x faster for updates.

The difference is significant enough, especially when we are rendering on low-power devices, such as our phones.. and there are a lot of crappy phones out there.

All of this to say that I'm worried about material migrating and using emotion by default, which will decrease the rendering performance of Material UI and sites using it by a 3-5x factor. (this is not actually true, it depends on the component; the more complex it is, the less difference there will be).

Some questions and food for thought:

  • Is the DX really worth the UX tradeoff? Even the DX is debatable as many prefer makeStyles
  • The problem with makeStyles has to to with dynamic styles which seems to be fixable by implementing a deterministic cache id. Currently, every component instance gets its own id for dynamic props, even if props and output styles are the same, causing runtime overhead, many style tags in the head, and decreased SSR performance. Seems fixable by using a hashing strategy for dynamic styles, just like many other CSS in JS libs are doing it. Related: https://github.com/mui-org/material-ui/pull/16858
  • Is there any room for improvement on emotion and styled-components to get closer, or even be better than makeStyles?
  • Will Material UI support an official JSS engine adapter so that developers can choose it for more performance?

I can live with a small performance loss.

Not a 300% performance loss, not at any cost. ๐Ÿ˜…

@satazor thanks for exploring this. We did a heavy perf testing before starting this effort, see PR https://github.com/mui-org/material-ui/pull/22173 for more details (we did it on the ListItem component) and the perf difference was at most 10% for rendering x1000 instances, in production mode.

https://deploy-preview-22173--material-ui.netlify.app/performance/list-raw/

https://deploy-preview-22173--material-ui.netlify.app/performance/list-mui/

https://deploy-preview-22173--material-ui.netlify.app/performance/list-styled/

https://deploy-preview-22173--material-ui.netlify.app/performance/list-styled-wrapper/

Based on this, we decided to ignore this difference, because of the benefits we would get (dynamic props out of the box, styled API that was already used by a big percent of the developers already etc - the whole summary can be find in the PR description :))

Not sure what is happening on your benchmarks, but 3-5x seems too much to me, it makes me wonder why would anybody use emotion/styled-compoenents if this was the case.. We can try to see where is the difference between the two benchmarks, in case we are missing something. Also, doing benchmarks on a real MUI component would be much better in my opinion, so we would get more realistic numbers, so let me know if you want to explore more on this side. The PR I linked is a good starting point.

Thanks for the reply @mnajdova. You are right that testing on a Mui component would be more realistic. What's probably happening is that the Mui code for the List components is the predominant slowness factor, and the difference between them (~30ms) is the actual rendering time difference associated with styles. I will take the code of that PR and add it to the benchmark, to see the results.

Will this matter in the end? Probably not, but it depends on the app. The performance difference between current Mui components and styled ones will increase as the rendering code itself is simpler. As an example, I'm expecting to see increased differences on the Icon or Typography components, but decreased differences for Cards. So, it really depends on the app and the amount of components of each type the app is using.

which will decrease the rendering performance of Material UI and sites using it by a 3-5x factor.

You've created a benchmark that has this factor. It does not follow that every single component will show this decrease. Please try to avoid jumping to conclusions since this misleading information spreads very easily from such a visible issue.

Using the devtools extension I saw 140ms for mount with emotion vs 120ms for mount with makeStyles.

You've created a benchmark that has this factor. It does not follow that every single component will show this decrease. Please try to avoid jumping to conclusions since this misleading information spreads very easily from such a visible issue.

You are right, see my previous comment.

You've created a benchmark that has this factor. It does not follow that every single component will show this decrease. Please try to avoid jumping to conclusions since this misleading information spreads very easily from such a visible issue.

Using the devtools extension I saw 140ms for mount with emotion vs 120ms for mount with makeStyles.

I've updated the benchmark to use actualDuration instead of baseDuration, so we should now see values more inline with what is shown in the devtools profiler. The baseDuration measures the time without memoizations, while actualDuration is the opposite. Please note that this change only affects the rerender performance, and now I'm seeing makeStyles 6-8x faster for rerenders, which means it has better caching/memoization? However, I'm not getting the values you are seeing. Can you try with the updated links?

Screenshot 2020-09-22 092415
Screenshot 2020-09-22 092514

@satazor I don't think that your test cases are equivalent. We should be good.

A couple of changes you can try to understand that and that will reduce the difference of performance:

  • Mounting react components has a cost, especially when rendering a lot of them. makeStyles uses div directly while emotion/sc create new components. In our table benchmark, I have experienced that each addition component adds the baseline rendering time, so if 3 levels of components => x3 (Or why <MenuItem> is almost x10 slower than a <li>).
  • Your demo with makeStyles creates a single stylesheet, emotion/sc don't. Try using a single stylesheet with CSS selectors to target the items in each card container. Or do the opposite, breakdown the main makeStyles stylesheet into multiple, one per item.

Indeed, @oliviertassinari. It seems the performance going forward with emotion/styled-components is more in the 1.5x-2x factor max even for simple components, such as Typography, which I have implemented here: https://codesandbox.io/s/css-in-js-comparison-forked-lr3sr?file=/src/components/EmotionTypography.js.

Check the production build & benchmark at: https://csb-lr3sr-7lp24bj5l.vercel.app/

makeStyles is 1.5-2x faster on mount and 3-4x faster on rerenders. This is potentially something to keep an eye on, but I guess the deviation will be much smaller for more complex components.

So, in conclusion, I'm no longer that worried about the performance and I'm looking forward to see how this effort will look like.

@mnajdova Here's the production build for the List test: https://csb-lr3sr-3zi42510w.vercel.app/?components=1000, copied of the PR you mentioned. Codesandbox link: https://codesandbox.io/s/css-in-js-comparison-forked-6s4nl

makeStyles seems 1.7x faster on mount, and 2.2x faster on rerender. Iโ€™m not getting the 10% difference you are seeing. Am I doing something wrong?

@satazor Interesting, thanks for putting it together. I have used this benchmark with #22435 to compare the performance of

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

vs (emotion).

import SliderStyled from '@material-ui/lab/SliderStyled';

vs (styled-components).

import SliderStyled from '@material-ui/lab/SliderStyled';

Capture dโ€™eฬcran 2020-09-23 aฬ€ 17 23 23
Capture dโ€™eฬcran 2020-09-23 aฬ€ 17 25 07
Capture dโ€™eฬcran 2020-09-23 aฬ€ 17 23 54

At this point, it's hard to tell why it's slower. The bottleneck might not be on the styles. And mind that I have run it in dev mode โš ๏ธ!

I have added two new pages to have a look at the performance of the WIP emotion version of the Slider:

Once built for production, the stats seem to be similar to https://github.com/mui-org/material-ui/issues/22342#issuecomment-697540546, it's about x1.6 slower (but CSS is fully dynamic). Note that you can play with it the WIP emotion version with:

Capture dโ€™eฬcran 2020-09-23 aฬ€ 18 56 01

Capture dโ€™eฬcran 2020-09-23 aฬ€ 18 55 48

Also, note that we know that we could make the current JSS version x1.1 faster by using makeStyles instead of withStyles: #15023.

@oliviertassinari in prod mode things get a bit faster, but I guess the differences stay there. You may click on deploy > vercel on code sandbox to quickly deploy with production build flags.

Looking at how makeStyles is implemented, I see how it is faster when you just use static styles:

  1. On every mount, attach gets called.
  2. If it's the first instance of that component type, stylesCreator.create gets called, which in turn calls fn specified in makeStyles(fn).
  3. On every other instance, stylesCreator.create is skipped because the ref count is > 0.

For rerenders:

  1. On every rerender, attach gets called.
  2. Afterwards, stylesCreator.create is skipped because ref count is > 0, so no work is done at all.

Please note that if dynamic styles are into play, the sheets would have been updated here, on every mount and rerender.

On the contrary, styled-components and emotion seem to run our styling functions on every component instance, which results in more CPU cycles and memory backpressure. I thought they could somehow do a multi memoize cache based on props combination, but this would assume that the styling functions are pure, which might not be the case, consider:

     const someContext = useContext(FooContext);

    return <div css={ { paddingTop: someContext.padding(1) } }>;

If my assumptions are right, it will be very hard to reach the performance level of makeStyles for static styles generation, as the way it works and how the API is designed allows for optimizations that styled or css API can't.

I see a couple of possible directions we could explore:

  • In the initial issue's description, we have only benchmarked the performance with the dynamic support of Material-UI. We could add an entry for the static case (what v4 uses). We could also have a case for react-jss static & dynamic. This should help us understand the scope of the style engine options available.
  • We can investigate where the bottleneck is. We could imagine have an adapter for jss, like we have for styled-components, and see if performance is better. It should be much worse with the dynamic version of JSS, but we could compare it with the static version. Maybe it's coming from the unstyled abstraction.
  • We could consider the slowdown a price worth spending for unlocking dynamic styles and unstyled. That if you want to render a long list, you will use virtualization or relying on nested CSS selectors.
    emotion and styled-components have proven to provide flexibility and to be fast enough by the industry, and so did React.

If somehow emotion exposed a useCss hook like it was requested in https://github.com/emotion-js/emotion/issues/1321 and https://github.com/emotion-js/emotion/issues/1853, we could retain makeStyles API and consequently, the benefits of "static CSS". However, we would keep using static CSS everywhere, to boost performance, which is not so "clean" but that's what we are already doing in v4.

Actually, with the ClassNames API, I think we could do a port of withStyles right now that would retain the benefits of the static CSS perf and the good performance of dynamic CSS that emotion has. If const css = useCss() existed, then it would be straightforward to create a useStyles API port as well.

The main advantages of keeping withStyles + makeStyles API which uses emotion under the hood are:

  • The migration that material has to do is pratically none, we would just need to make the port of those 2 functions.
  • Users already using withStyles and makeStyles outside of Material UI, wouldn't need to migrate at all. They would get the benefit of improved performance for dynamic CSS, for free.
  • No additional logic is needed to keep the classes + CSS API. With styled we would need to create the global classNames by hand, or with a util.
  • In this strategy, useCss is our adapter function for CSS in JS solutions, instead of styled.
  • To support other CSS in JS solutions, they would need to provide a useCss hook or similar. Ideally, we would be able to do webpack / babel aliasing to switch them, just like styled.

We have explored the performance issue further in https://twitter.com/olivtassinari/status/1309247827839680512. I think that we can move forward as is. For teams that care deeply about performance, they can opt-in into the unstyled components, they provide better performance. For the others, like most of the industry, emotion and styled-components are good enough.

  • native: 7.71ms
  • v5 unstyled: 20.89ms
  • v4: 29.93ms
  • v5: 37.37ms
  • antd: 53.48ms
  • chakra: 64.67ms

https://codesandbox.io/s/slider-comparison-forked-jziv6?file=/src/App.jsโ€ฆ

Sorry that I bother you here so late in the discussion but I'm a bit surprised you are not looking closely at styled-jsx

Their list exactly met your requirements:

  • Works server and client
  • Full CSS support, no tradeoffs in power
  • Runtime size of just 3kb (gzipped, from 12kb)
  • Complete isolation: Selectors, animations, keyframes
  • Built-in CSS vendor prefixing
  • Very fast, minimal and efficient transpilation (see below)
  • High-performance runtime-CSS-injection when not server-rendering
  • Future-proof: Equivalent to server-renderable "Shadow CSS"
  • Source maps support
  • Dynamic styles and themes support
  • CSS Preprocessing via Plugins

I would pay attention to the fact that it is almost a shadow CSS standard API. So by removing jsx attribute you are good to transition to web components in the future which are working already in the normal browsers BTW.

Yes I know it might be not the most popular one.
But I just want to point out that Flash was very popular some years ago.
But it dried out at the end not being web standard compliant, and we have SVGs now.
Just for the record SVG standard was present long when Flash was ruling the industry.
I just look at historical events as good lessons and would try to spot the pattern that popularity is the last indicator of being bulletproof maintainable and future proof.

@mifrej I personally had a bad experience with it: https://github.com/vercel/styled-jsx/issues/142.

We have explored the performance issue further in https://twitter.com/olivtassinari/status/1309247827839680512. I think that we can move forward as is. For teams that care deeply about performance, they can opt-in into the unstyled components, they provide better performance. For the others, like most of the industry, emotion and styled-components are good enough.

  • native: 7.71ms
  • v5 unstyled: 20.89ms
  • v4: 29.93ms
  • v5: 37.37ms
  • antd: 53.48ms
  • chakra: 64.67ms

https://codesandbox.io/s/slider-comparison-forked-jziv6?file=/src/App.jsโ€ฆ

Did you try the babel plugins for emotion / styled-components? Did it make a difference to the timings?

What would a change from JSS mean for the classes prop available on existing MUI components? How would a v5 migration look for existing users that leverage this prop extensively?

We envision people to use the following syntax instead: https://next.material-ui.com/components/slider-styled/#unstyled-slider the classes are basically replaced by the class selectors available for each slot in the component. You can take a look also on the customization example: https://next.material-ui.com/components/slider-styled/#customized-sliders

As an advantage you can just use the props and apply dynamic styles based on them.

We envision people to use the following syntax instead: https://next.material-ui.com/components/slider-styled/#unstyled-slider the classes are basically replaced by the class selectors available for each slot in the component. You can take a look also on the customization example: https://next.material-ui.com/components/slider-styled/#customized-sliders

As an advantage you can just use the props and apply dynamic styles based on them.

I love this API! Very much a welcome change, and it seems to play really nicely with the style engines.

Before v5, if I recall correctly, the JSS compiler would mangle those class names and you couldn't reliably target them? But now it seems they'll be exposed for targeting purposes? ๐Ÿ™Œ Also, there was the issue with CSS precedence. And those concerns are resolved with this new approach? Thank you for working on this refactor!

@ConAntonakos exactly these classes can be targeted for both the unstyled and styled versions of the components. The order of which styled is invoked ensures that the new styles will win, of course if the specificity is the same :)

Before v5, if I recall correctly, the JSS compiler would mangle those class names and you couldn't reliably target them?

You could already target them in v4.

the classes are basically replaced by the class selectors available for each slot in the component.

Are they "basically" replaced or actually replaced? I thought we decided to keep some of the old API to reduce the number of breaking changes.

I thought we decided to keep some of the old API to reduce the number of breaking changes.

We decided to keep the same API for the theme.components.overrides - overrides defined in the theme work in the same manner.

For the instance overrides, we have the styled approach now with the class selectors, that is why the classes prop is not supported anymore. Developers can do that same in more flexible manner now.

Developers can do that same in more flexible manner now.

That sounds like it's such a minor issue because there's an alternative but the migration cost for that is huge. How does the migration plan look like?

Developers can still use the theme overrides, if they just move the instance overrides to a nested ThemeProvider they wouldn't need to change the styles defined at all, as the structure between those two is the same (it's not perfect, but if they want incremental upgrade it is a way to go)

On the other hand, we can still support the classes prop easily, but in that case we cannot guarantee if the combination of classes + styled is used on the same component which one should win. Should we backport the support of classes at least on the styled version of the components?

I may have missed this along the way through this thread - another related question to my classes question. What sort of "correctness" guarantees might there be?

For example i've edited the slider example:

const StyledSlider = styled(SliderUnstyled)`
  & .MuiSlider-raill {
    display: block;
    position: absolute;
    width: 100%;
    height: 2px;
    border-radius: 1px;
    background-color: currentColor;
    opacity: 0.38;
  }
)

You'll notice i accidentally misspelled MuiSlider-rail. Previously with classes there would be something like classes.rail, and if i misspelled the property, i would get a runtime warning that classes.raill doesn't exist in the stylesheet. I believe the Theme had the same behavior?

The advantages of the classes API I can think of:

  1. Edge against the global CSS selectors that leaks between children: e.g. In .css-e7ylz8 .MuiTreeItem-group. You have no guarantees that the component doesn't apply the style on a child component (not the effect you were expecting, a surprise!). Probably OK-ish for our components as we will be careful.
  2. Make handling portals a no-brainer: https://material-ui.com/guides/interoperability/#portals. For styling the tooltip, we will need to style the popper component, not the root as we did with the Slider.
  3. The $styleRule syntax warns if the style rule is undefined.
  4. Allow customizing the class names used. The current solution is to use the componentsProps prop. We merge the class names.

There is an alternative world where we would:

a. Allow to solve 1. and 3. with styled component selectors.
b. Expose the classes API for backward compatibility.
c. In order to get a. and b. working, we would need to flatten how the styles of the component are written internally. x styled component instead of 1 styled root. But โš ๏ธ with the perf implication.

Do we really need to do that?


I have looked at the history of jQuery UI's classes prop. I could find two issues: https://bugs.jqueryui.com/ticket/7053, https://bugs.jqueryui.com/ticket/8928 and the initial commit: https://github.com/jquery/jquery-ui/commit/c192d4086d9bbaf09d5f870857af30c60a427e22.

We envision people to use the following syntax instead: https://next.material-ui.com/components/slider-styled/#unstyled-slider the classes are basically replaced by the class selectors available for each slot in the component. You can take a look also on the customization example: https://next.material-ui.com/components/slider-styled/#customized-sliders

As an advantage you can just use the props and apply dynamic styles based on them.

Wow, this is the best thing ever.
Unstyled or Headless components is going to be the best thing for customization (one of the critics about mui, I think).
This is not a little thing to fix imo, but a hude plus for MUI.

PS : I remember some hard time customizing some components in the past
PS2 : Did you take a look at https://github.com/modulz/stitches ?

You'll notice i accidentally misspelled MuiSlider-rail. Previously with classes there would be something like classes.rail, and if i misspelled the property, i would get a runtime warning that classes.raill doesn't exist in the stylesheet. I believe the Theme had the same behavior?

@ianschmitz in addition to the option @oliviertassinari suggested for using the styled components selectors, we have another option, which is to expose constants for the classes that we expose. Maybe something like:

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

const StyledSlider = styled(SliderUnstyled)`
  & .${sliderClasses.rail} {
    display: block;
    position: absolute;
    width: 100%;
    height: 2px;
    border-radius: 1px;
    background-color: currentColor;
    opacity: 0.38;
  }
)

It's not the same as the classes.rail runtime warning, but should help against misspelling the classes.

@mnajdova We could also consider an eslint plugin.

Regarding the support on the classes prop - in order for us to be able to reliably do this, we will have to create components for each part (slot) of the core component. For example, for the Slider, we would need to create components for the rail, track, mark, mark label, thumb and the value label. This will allow us to specify the styles directly on those components, instead of using classes for increasing the specificity. These changes can be find on this PR - https://github.com/mui-org/material-ui/pull/22893

With these changes, we've created a codesandbox, that can compare the perf of this new updated Slider component compared to the v4, native styled, unstyled, as well as two other competitive libraries - https://codesandbox.io/s/slider-comparison-with-multiple-components-2tytc?file=/package.json

If we compare these perf with the perf of having only one component and using the classes selectors for the parts of it - https://codesandbox.io/s/slider-comparison-forked-jziv6?file=/src/App.js we will notice that adding components per slot has an 20% perf degradation ๐Ÿ˜ข

Production versions:

To be honest I don't know at this point whether it's better to add this backward compatibility or not.

Are these any real use-cases for the the support of the classes or is it just for easing up the upgrade?
Are we ok with having 20% perf degradation only for easing the migration path?
Is there some alternative that we cannot see, that would help us to do both of these without paying the perf cost?

@ianschmitz @eps1lon @oliviertassinari and others :) are there any thoughts on this?

As long as we can define and use themes & styles with TypeScript, I wouldn't mind spending time migrating compared to losing 20% perf

I'm generally curious, and forgive me if I don't understand the underlying design, but classes was an API for JSS? If the API design is switching from JSS to styled components, does it make sense to keep supporting classes?

Are these any real use-cases for the the support of the classes or is it just for easing up the upgrade?
Are we ok with having 20% perf degradation only for easing the migration path?
Is there some alternative that we cannot see, that would help us to do both of these without paying the perf cost?

Apologies if i've missed anything with the proposed API, but IMO the use case I most often see within our org is the abstraction of the underlying styling system used by MUI. Yes i guess classes is sort of an "API for JSS" as @ConAntonakos mentioned, but to the consumer it didn't matter. As a consumer I could use the CSS tech of preference (are there any limitations today with classes?). We have a number of products using a variety of:

  • Vanilla CSS files
  • SCSS
  • JSS

depending on the needs and preferences of those teams.

Exporting the class names helps if you're using some flavor of CSS-in-JS, but what are the thoughts for those that aren't?

RE. 20% perf, i agree that is likely not an acceptable trade-off. At the end of the day you guys should do what's best for the majority of the community. Just offering my thoughts ๐Ÿ˜„

One wish I never got was that material-ui would be react-native compatible. Many projects are cross platform these days and having a unified styling solution offers a lot of advantages for cross platform components. We ended up rolling our own using material-ui on the web side and react-native-paper on the native side and standardizing on the material-ui API. I'd be interested to understand if this new styling solution will make the use of (some/all) Material UI components on react-native a possibility? Any window references in components would obviously be a blocker, but the styling itself supports native as well doesn't it?

@mschipperheyn react-native is a no-goal so far. It's +5% of usage potential (1 month of growth) and +50% more effort (a guess). The opportunity cost is really high with no obvious direction on how to monetize it (outside Ionic's model). Also, mind that flutter seems to have captured a large chunk of the audience that react-native targets.

Now that v5 is in alpha release, is there a resolution to this issue? In particular, is the styling solution still based on JSS? We've seen substantial performance issues related to JSS, so have been eagerly anticipating a new solution.

@zzzev This isn't an issue per se. It's an RFC (Request for Comments) thread.

I'm inquisitive about which substantial performance issues you're talking about and how going from JSS would improve that. Because the way I see it is that if you have performance problems, it's probably not the styles but how they're implemented, i.e., the same result could be achieved by writing the styles in another way, using less processing power.

At the very least do I fail to relate to how going from JSS to Emotion (which is shown in this thread to _most likely_ have some performance degradation) would improve anything.

My understanding is that emotion will cause a slight hit to static styles performance, and much better dynamic styles perf -- but maybe that's not quite right? There are a lot of different numbers in this thread and it's hard to reconcile into one picture of performance (and obviously it depends a lot on an individual's situation).

When you say we should write the styles in a different way, do you mean avoiding dynamic styles? Or are there other best practices we should be considering?

@zzzev Correct on the first part, not quite on the second (unless you count going from unsupported to supported as an infinite performance gain ๐Ÿ™‚).

Emotion enables support for dynamic styles, at the cost of moderately slower performance for static.

I'm confused; aren't there dynamic styles in the current/v4 makeStyles? e.g. this pattern

I'm confused; aren't there dynamic styles in the current/v4 makeStyles? e.g. this pattern

There are but there's a big known perf issue. My team stays far away ATM

I just hate component-styled
jss is good but there are a few problems in debugging, performance and still some bug still not resolved such as nested like&:after

it's my packages build-up for react-native and react-native-web
https://www.npmjs.com/package/@material-native-ui/theme-provider

I`d like something like this (it's build-up on top of RN and RNW)

So is there a conclusion on the recommended style solution to use with Material UI v5? It seems that there is an intention of moving away from JSS which @material-ui/styles is currently built upon. Or @material-ui/styles will be refactored to build upon other styling solutions like styled-components instead?

So is there a conclusion on the recommended style solution to use with Material UI v5? It seems that there is an intention of moving away from JSS which @material-ui/styles is currently built upon. Or @material-ui/styles will be refactored to build upon other styling solutions like styled-components instead?

@matthewkwong2 we wouldnโ€™t adopt the @material-ui/styles package to the new styled engine, it will keep supporting jss. In v5 it will be isolated to the rest of the codebase and we plan to remove it in v6, meant to help transition to the new styling engine.

For v5, we will have new beat practices regarding the styling solution, like the sx prop and the styled utility, we still work on the documentation around this.

Also the theme overrides and variants are still going to be supported in v5.

For v5, we will have new beat practices regarding the styling solution, like the sx prop and the styled utility, we still work on the documentation around this.
Also the theme overrides and variants are still going to be supported in v5.

So if I understand correctly, for the styling of individual components, it is encouraged to use sx or styled instead of makeStyles.

But what about the theme (i.e. createMuiTheme)? I believe that part is also built upon JSS right? What would be the recommended way to create a theme (i.e. with the main purpose of defining global styles) now?

We still keep the same structure for creating the theme, we just expect that the values for the components overrides & variants follow the syntax for emotion/styled-components. There are no changes in how the theme is created, the API is exactly the same, the same theme context is reused for the new styled engine too. Does this makes sense @matthewkwong2 ?

@mschipperheyn react-native is a no-goal so far. It's +5% of usage potential (1 month of growth) and +50% more effort (a guess). The opportunity cost is really high. Also, mind that flutter seems to have captured a large chunk of the audience that react-native targets.

I don't want to take us on too big of a tangent, but I would like to push back on some of these rationales.

  • In the past when building RN apps one of the hardest things to deal with was Material Design. In fact it was a big enough issue it could potentially decide whether to bother building a mobile app. I hear it's a bit better with one promising new library. But I doubt it's as promising as it would be if MUI was in the mix. Maybe it's just me, but I could see a cross-platform MUI actually increasing RN's usage. I know that it's only a small fraction of react-dom's usage, but web is enormous and react-dom dominates modern web frameworks. Even if react-native usage is relatively small, that can still be a fair number of people as an absolute who would use it. A poll of current MUI users would probably be a better metric than npm stats.
  • I know I'm out of the loop, but I don't quite buy Flutter taking over React Native. That traffic comparison isn't really a great way to compare. It's affected by so many confounding factors (Flutter is newer so more people already know RN and don't need to reference the documentation; Flutter's documentation could be more useful re-referencing increasing it's traffic; Some of RN's doc reading traffic could also be going to Expo instead). This somewhat flawed Google Trends comparison is almost a better measure, and it's fairly even. I know personally Flutter had a few hard deal breakers and RN is still much more promising for the next time a client says they want a mobile app.
  • JSS was one of the biggest issues (actually possibly the biggest) with making MUI work with React Native. I know there will still be a few complicating factors, but IMHO the switch to emotion has likely eliminated 2/3 of the difficulty in making MUI work in RN at least experimentally.

the switch to emotion has likely eliminated 2/3 of the difficulty in making MUI work in RN

We look forward to seeing your POC ๐Ÿ˜„

We look forward to seeing your POC ๐Ÿ˜„

I'd love to play around with it if I have a chance. But people generally don't bother making POCs for things that maintainers show a disinterest for. No point building something when the current atmosphere is that it'll probably just end up abandoned. Hence why I want to push away from dismissing the value of RN or the value of RN as a possibility for the future.

Two questions:

  1. Can you use the new styling solution without HOCs? I for one love the React hook API that MUI currently has... not sure if the styled syntax is also a HOC under the hood but that would beat efforts (pretty much everywhere in React libraries) in moving away from HOCs.
  2. Will the classes prop most components have still be supported (it would give full flexibility in what styling solution users can go with)?

the fast-refresh is enabled by default in create-react-app v4. should we add fast-refresh support as a new requirement?

Trying this out with gatsby. When I do import { Link } from '@material-ui/core' I got:

Can't resolve '@emotion/core' in 'node_modules/@material-ui/styled-engine'
File: node_modules/@material-ui/styled-engine/index.js

Can't resolve '@emotion/styled' in '/node_modules/@material-ui/styled-engine'
File: node_modules/@material-ui/styled-engine/index.js

When changed to import Link from '@material-ui/core/Link' problem gone.

If I install @emotion/styled @emotion/core I got:

Can't resolve '@emotion/react' in '/node_modules/@emotion/styled/dist'

Then I install @emotion/react.

A runtime error comes in.

Error: The `@emotion/core` package has been renamed to `@emotion/react`. Please import it like this `import { jsx } from '@emotion/react'`.
./node_modules/@emotion/core/dist/emotion-core.cjs.dev.js
node_modules/@emotion/core/dist/emotion-core.cjs.dev.js:3

Involved package versions:

    "@emotion/core": "^11.0.0",
    "@emotion/react": "^11.0.0",
    "@emotion/styled": "^11.0.0",
    "@material-ui/core": "^5.0.0-alpha.15",
    "@material-ui/lab": "^5.0.0-alpha.15",

Install v10 versions of Emotion packages

Oh 11 is the "latest" version now so I think mui need to upgrade or document that

Oh 11 is the "latest" version now so I think mui need to upgrade or document that

We have it documented via version ranges in peerDependencies. We didn't mention it explicitly in the install instructions since we plan to update it to v11 soon.

Friendly reminder that this is an alpha and emotion 11 just a few days old. As an early adopter you should expect some rough edges.

Hello everyone, I'm new here, and I was looking at material-ui css solutions and came at this issue.
Just giving my 2 cents on this, I'd like to suggest Linaria https://github.com/callstack/linaria.
It's a zero runtime library, with css extraction and CSS variables as React props.

Hope this helps on this RFC ๐Ÿ˜„ .

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ericraffin picture ericraffin  ยท  3Comments

iamzhouyi picture iamzhouyi  ยท  3Comments

revskill10 picture revskill10  ยท  3Comments

sys13 picture sys13  ยท  3Comments

rbozan picture rbozan  ยท  3Comments