--- robots: noindex, nofollow tags: pitch --- For instructions on shaping a project see here: [Shaping a Project](/kX02SXVbS6KzMOQd56i6Cg) # Improve react-northstar performance (css expand during build time) ## Problem Performance of CSS-in-JS is the most complicated question. Currently we have a lot of requests from Teams side to improve performance somehow immediately as Fluent components are widely in Teams Modular Packages. ## Appetite If we will be able to reach reasonable performance level it can comparable with proposed CSS solution. ## Solution The solution is separated to three parts: - improve Fela performance - improve Fela plugins performance - try to pregenerate some rules at runtime ### Fela tweaks Fela by itself is quite fast, but I was able to find slow functions there and improved perfomance (improves render time for 5-7%) of one of them. Goal there to merge existing PR [(robinweser/css-in-js-utils#11)](https://github.com/robinweser/css-in-js-utils/pull/11) and try to find out other places to improve. ### Fela plugins performance We have already done optimisations there, for example we changed a dependency in one of plugins ([microsoft/fluent-ui-react#1925](https://github.com/microsoft/fluent-ui-react/pull/1925)) and gained 10% improvement. Currently our slowest plugin is `felaExpandCssShorthandsPlugin` what expands shorthand properties to longhand (`padding => padding-left, padding-right, etc.`): ![](https://i.imgur.com/yFiIppm.png) The worst thing that we can't improve its performance in anyway except of its removal. #### Step 1: create helpers _So how we are going to expand properties?_ Via helpers like [`padding()`](https://polished.js.org/docs/#padding): ```diff const styles = { - padding: '5px', + ...padding('5px'), } ``` In this case we can keep existing behavior and disable that plugin, that will give us around 25% gain. The set of properties is limited to: - `border`, `borderTop`, `borderRight`, `borderBottom`, `borderLeft` `borderWidth`, `borderStyle`, `borderColor` - `padding`, `margin` - `outline` - `flex` #### Step 2: babel transform To avoid runtime costs as each helper is a function I propose to write Babel plugin to tranform them: ```diff const styles = { - ...padding('5px'), + paddingLeft: '5px', + paddingBottom: '5px', + paddingRight: '5px', + paddingTop: '5px', } ``` This will allow us to the same job as the expand plugin without any runtime cost. ### Pregenerate styles The next step is try to pregenerate some of static styles. Some of our rules are static and can be transformed during build time: ```js const styles = { paddingLeft: token.padding, // dynamic paddingRight: '5px', // static } ``` During `renderRule()` Fela: - runs `generateDeclarationReference()` to create a key to for cache entry - runs `cssifyDeclaration()` to tranform JS styles to CSS values - generates classes, inserts to a cache and notifies DOM consumers _What if we will be to omit first & second stages?_ ```js const styles = { __a: { declaration: 'padding-right5px', css: 'padding-right: 5px' }, } ``` And in custom enhancer handle these cases. At least it worth trying. ### Risks (Rabbit holes) #### Fela tweaks __Risk:__ Robin will not accept my PR __Solution:__ Finalize enhancer on our side #### Fela plugins performance __Risk:__ Development of Babel plugin can take too many time __Solution:__ Finish first step and only then switch to second #### Pregenerate styles __Risk:__ It will not work __Solution:__ Ensure that merging of objects will continue to work ```js const styles = (props) => ({ display: 'block', ...props.inline && { display: 'inline-block' } }) ``` __Risk:__ There will be no perf improvement __Solution:__ - Measure amount of static styles - Test perf improvements before trying to automate transforms ### Out of scope (No-gos) ???