Why is the `sx` prop so much slower? - reactjs

As per MUI's own doco, and this answer - components using sx render significantly slower than components using other styling mechanisms.
On the surface, it looks like sx is just an alternate convenience API for doing the same thing - so I wouldn't expect it to have such a different performance profile.
My question is: Why is the rendering of a component using sx so much slower - what's it doing so differently? Is it a whole different styling engine or something?
I'm curious about the possibility of optimising it, or coming up with a compromise that retains most of the usability but omits whatever feature is causing the slowdown.
Please note, this question is about "why is the performance so different" - not "why do you think the difference doesn't matter".

As I started to dig into this, I realized that I needed to measure the performance of different scenarios in order to have any confidence in my understanding of the performance aspects of the sx prop.
I believe that the performance information in the MUI documentation was gathered using some variation of this repository: https://github.com/mnajdova/react-native-web. The react-native-web repo was used as a starting point because of its "benchmarks" package which contains a useful framework for measuring the performance of different React element rendering/styling approaches.
I created my own version here: https://github.com/ryancogswell/mui-style-benchmarks. You can use this as a starting point to dig into this further. Below are the measurements I made and my conclusions.
My Results for the "Mount deep tree" Benchmark
This test renders 639 elements with approximately 17 CSS properties each except for the cases ("..._minimal", "..._medium")
which reduce the number of CSS properties to show the performance impact.
Styling Implementation
Time in ms
Implementation Desc
inline-styles
22.78
No styling engine, just use style prop
mui_sx_full
36.89
MUI Box sx prop with 17 CSS properties
mui_sx_medium
24.09
MUI Box sx prop with 9 CSS properties
mui_sx_minimal
18.15
MUI Box sx prop with 4 CSS properties
mui_styled_box
22.38
MUI styled MUI Box with 17 CSS properties
mui_styled_box_minimal
17.90
MUI styled MUI Box with 4 CSS properties
tss_react_makestyles
17.10
makeStyles from tss-react with 17 CSS properties
mui_styled
16.93
MUI styled div with 17 CSS properties
mui_styled_minimal
13.77
MUI styled div with 4 CSS properties
emotion_styled
16.69
Emotion styled div with 17 CSS properties
emotion_styled_minimal
12.76
Emotion styled div with 4 CSS properties
emotion_css
12.58
Emotion css div with 17 CSS properties
Conclusions
MUI styled (e.g. import {styled} from '#mui/material/styles') only adds a small amount of overhead
to Emotion's styled.
tss-react performs similarly to MUI styled.
Emotion styled, Emotion css, MUI styled, and the MUI sx prop are all more expensive when there are
more CSS properties passed to the styling engine.
The performance of the sx prop degrades more quickly than the styled API as more
CSS properties are passed to it. With 17 CSS properties the performance is much worse than the styled API (2x).
The sx prop performs just fine for a small number (e.g. < 5) of CSS properties. Particularly, if you
are already using a MUI component in a given situation, there is no meaningful performance difference
between wrapping it with styled or using the sx prop if you are just using a small number
of CSS properties.
What is the cause of the sx prop slowness?
Is it a whole different styling engine or something?
It is not a different styling engine. The output of the work done for the sx prop is fed into the styled API of the main styling engine (e.g. Emotion or styled-components); so using the sx prop with the Box component is guaranteed to be slower than the equivalent styles using styled on a div because the sx prop still uses styled in the end but does additional work first.
What is the additional work done by the sx prop?
The MUI styled API calls styleFunctionSx in order to transform the CSS properties in the sx prop to the form expected by the styling engine.
styleFunctionSx traverses all the CSS properties in the sx prop
For each CSS property, it checks to see if it has a style function for transforming the prop value. These style functions provide support for shorthand notation for some properties such as padding and margin and support for using theme spacing units instead of pixel values.
Each of the style functions leverages the style function in https://github.com/mui/material-ui/blob/v5.5.3/packages/mui-system/src/style.js to handle some common transformations of prop values including the support for breakpoint-specific values.
The net effect is that for each CSS property there are a number of lookups and function calls to see if the CSS property needs to be transformed even in the cases where the value passes through without changes.
I'm curious about the possibility of optimising it, or coming up with a compromise that retains most of the usability but omits whatever feature is causing the slowdown.
I'm sure that performance improvements are possible for the sx prop, but I don't think there is any single silver bullet for easily making it faster. Instead it will probably require a large number of little changes that are each barely measurable, but cumulatively provide decent improvement. The challenge is to make those changes without simultaneously making the code more complex and/or harder to maintain and/or more error prone.
The main compromise that "retains most of the usability" is to use Emotion's css prop directly. It can be used directly on elements in a similar fashion as the sx prop -- you just lose the shorthand notations and theme lookups that the sx prop provides. The theme lookups (e.g. for colors or spacing units) are easy to get directly from the theme by using the useTheme hook in the component. The theme.breakpoints API can be used instead of the breakpoint shorthands; though the sx breakpoint features are much nicer from a DX standpoint.

Related

Style MUI component but have it's style overwritten by passed ThemeProvider

I want to style my custom React/MUI components by default, but still have the parent MUI ThemeProvider be able to override my styles.
For example, let's say that I have a Button that is green by default (styled in my code), but it should be able to have the colour overridden by a default theme passed down via ThemeProvider.
I can't seem to find anyway to do it as any styling I apply in my component becomes the default as it's the last in the CSS/styling tree.
I couldn't find much information in the documention or github issues about that topic — I can just tell you how to cope with the situation and how it works currently in v5.
It seems like the priority is as follows:
sx > styled() > theme
ie. sx has the highest priority.
At first it seems quite strange that you cannot overwrite something with your theme, but on the other hand you'd also like to be able to overwrite the theme with sx occasionally. So to me the priority makes sense.
You should not use !important in the theme if possible, because it prevents sx and styled() from doing its job.
What you'd do is ship reusable components unstyled (no styled() or sx) and style them via the theme.
Perform the colorization in the theme. Width, height, margins and paddings you can also define via styled() and/or sx if you don't need to change those in the theme.

Replicate MUI paper background image

I'm working with Material-UI on my NextJS project, and wanted to know if is there a way to replicate the paper background-image on other components ? It will allow me to make some other components the exact same colors as Paper, because this background-image affect the color with a lighter effect.
EDIT: this effect seems to only occurs on dark mode
Thanks
Some components like Card, Dialog, Menu (and more) already use Paper as an inner component, then they get its style. The best way to add it to your components is to just use Paper as a surface.
You can also add variants to Mui components - for example, you add paper variant to TextField, and there add the styles.
A simpler way (but not complete) is to directly add the Mui-Paper className to another component (it is not complete because there are other styles except Mui-Paper. To achieve the elevation background-image effect you mantioned, you should add .MuiPaper-elevation{props.elevation} className.

In Material-UI v5, should I prefer the css prop over the sx prop or vice versa?

I believe these two props both use emotion under the hood and they seem to do much the same thing. I've been using the css prop because I prefer template tags and real css vs. javascript style names for css properties. Is there any reason to prefer one over the other given that they are basically interchangeable in terms of functionality?
I read the page about the new MUI styles system here: https://next.material-ui.com/system/basics/
And it feels to me that the main difference is the following:
The css prop lets you write something that looks like classic css, as you would do in actual CSS, or Less/Sass, or styled-components
The sx prop gives access to the 'system' which is a set of utilities to quickly access props with shortcuts, as well as theme properties, which already existed before v5, but are now even easier to use.... after some learning curve...
The above documentation page gives a lot of examples.

Material-UI Box Component Import Order and Style Tag Generation

I'm using the Material-UI (Mui) Box component to save time. It works great most of the time, but I sometimes run into issues when the styling clashes with the default styling of other Mui components. For example, I'm using the Box component to override the default padding-right of the Mui Toolbar (see picture below).
The problem and my question lie in the way the style tags are generated from this code. The style tags generated by this Box component are being overridden by the style tags generated by the Toolbar component (see picture below).
Additionally, when I inspect these style tags the MuiBox tags are definitely inserted before the majority of the other style tags and they seem to be empty. (see pictures below).
I've read the docs over and over, and tried many things. The most promising thing I could find was in the documentation for the Box component:
⚠️ The CSS specificity relies on the import order. If you want the guarantee that the wrapped component's style will be overridden, you need to import the Box last.
I tried this but still have had no success (see image below).
I know there are several ways to get around this (e.g., makeStyles, withStyles, inline styles, !important), but these strategies make using the Box component somewhat pointless in many situations. So my question is how do I make the MuiBox style tags have the highest priority (inserted last) and why are they empty when I look at them in DevTools?

where can I find the list of allowable props or attributes for each component supported by styled-components

Starting our in react, it is easy to find tutorials that show how to start with react and styled components. I find it is easy, but it is hard to get the styling I want because I cannot find a dictionary equivalent that shows the allowable list of styled-components with a internal list of the allowable attributes or values.
For instance (only) the button component is very common, but none of the documentation shows the allowable values of say the type attribute. how would you know that the type attribute exists, and then know that there are only three allowable values button|onsubmit|reset.
Can anyone point me to a useful dictionary or cheatsheet for styled-components as used with React.
It seems you cannot find options through a typescript autocomplete method in vs code.
I realise that styled-components is a very useful layer over javascript over html and css, but is there a relevant and complete dictionary of allowed values and components - it does not seem to exist in styled-components documentation.
The lack of a reference doc for styled-component values makes it hard for me as a new starter.
While styled-components wraps CSS code it has no predefined components.
Utilising tagged template literals (a recent addition to JavaScript) and the power of CSS, styled-components allows you to write actual CSS code to style your components. It also removes the mapping between components and styles – using components as a low-level styling construct could not be easier!
Refer to MDN HTML elements reference for the list off all HTML elements and their properties.
Also, if you styling a custom component, you should be aware of its properties within the corresponding documentation (if the component from a library), or use PropTypes, Flow or TypeScript.

Resources