I like the convenience of CSS-in-JS especially being able to co-locate styling but I’m not convinced on a few of things:
That hashed classes are a “must have” since name clashes are easily avoided in apps with good (and readable) naming, and arguably annoying on 3rd party components when you just really needed to select an element with a nice semantic class but can’t.
That turning your CSS into a tangled mess of props and logic is all that good of an idea, which can make it less readable but also leads to terrible, and I mean terrible loss of performance.
That you have to fiddle with build tools (restricted in Create React App for example) to try and optimize the CSS. Which by the way doesn’t do anything clever like optimize parsing times; it just minifies, removes comments, adds source maps etc.
At first you go on your merry way and don’t notice any performance problems with these “blazing fast” runtime CSS solutions. That is until you put a bunch of them on one page in Storybook.
Let’s look at the ubiquitous Button component as it has various styles and options:
We then had a bunch of simple and fast functions to compose the styles. We even optimized the static css to be it’s own chunk (sharedStaticButtonStyles):
Our Button component had the most dynamic function calls and the most elements in Storybook. Let’s see how it performs to load.
That’s around 36 seconds spent on Emotion parsing (see red underlines). The Performance tool makes it about 2–3x as long, which is still far too long.
It gets worse
I initially blamed a Tooltip component for being slow to show itself, then realized it was only when attached to a Button. It was then that I realised Emotion is parsing the CSS again on hover I guess because the Tooltip causes a re-render, and freezing the main thread for about 900ms before the tooltip could show:
You might be thinking “well my components aren’t that complicated or slow”. Even if they are twice as fast for me on a large app with many components on a page, spending 1–2 unnecessary seconds frozen on load while CSS is parsing is unacceptable. Especially on the kind of apps that I build which are highly dynamic and receiving many updates per second (which Emotion is re-parsing every render like it does on hover due to the dynamic nature of the props based styling).
So how can we make and keep things fast?
Firstly if you’re starting a new project you may want to consider compile-time CSS-in-JS solutions: