question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. ItĀ collects links to all the places you might be looking at while hunting down a tough bug.

And, if youā€™re still stuck at the end, weā€™re happy to hop on a call to see how we can help out.

[RFC] v5 styling solution šŸ’…

See original GitHub issue

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 šŸ‘

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 šŸ‘. 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?

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:275
  • Comments:239 (128 by maintainers)

github_iconTop GitHub Comments

41reactions
Andaristcommented, Aug 24, 2020

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.

28reactions
shilangyucommented, Aug 24, 2020

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

Read more comments on GitHub >

github_iconTop Results From Across the Web

[RFC] v5 styling solution šŸ’… Ā· Issue #22342 Ā· mui/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Ā ......
Read more >
styled components not working in prod nextjs - You.com
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....
Read more >
@mui/styles (LEGACY) - MUI System
A CSS-in-JS solution overcomes many of those limitations, and unlocks many great features (theme nesting, dynamic styles, self-support, etc.). MUI's styling ...
Read more >
@mui/material | Yarn - Package Manager
Fast, reliable, and secure dependency management.
Read more >
CHANGELOG.md Ā· č„šęœ¬ęµ‹čƕ11/material-ui - Gitee.com
Versions. 5.0.0-alpha.20. Dec 21, 2020. Big thanks to the 13 contributors who made this release possible. Here are some highlights āœØ:.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found