Why or why not choose CSS-in-JS
Local Scoping vs. Readability; Encapsulation vs. Flexibility Advanced Functionality vs. Learning Curve, Minification and others
CSS-in-JS has gained increasing popularity recently. Before I tried it, I already heard about all the happy stories and 5-star comments about it. So then I jumped in — it was a pretty fun experience — but I also have to admit that it’s not that glamorous and hassle-free as I thought.
So I just want to post here some highlights and a few concerns that you should think ahead before you shift your project from traditional CSS to CSS-in-JS.
With this trend, the traditional CSS stylesheet becomes less ideal as it’s difficult to tell the order of each individual stylesheet being load and can lead to style for component A overriding that of component B if naming and specification is not properly managed.
But with CSS-in-JS, it becomes less of an issue (Actually no issue at all!). Since now the CSS style couples with its JS file in one component, making adding and removing a certain component posing no conflict with other styles.
So here are some valuable attributes CSS-in-JS bring to the table, and we are also going to discuss the drawbacks as well.
Local Scoping vs. Readability
Traditional CSS is “cascading style sheets”, that means, it is global by default. So as the example above mentioned, you might have some sub stylesheet overrides your other ones and ends up undesired behaviour.
So CSS-in-JS solves this problem completely by either of the 2 solutions:
- Generating 100% unique selector to the element
- Creating in-line style to the element on the fly
For example, in the Material-UI I uses, I have a
<li/> tag, and the autogenerated classname is:
MuiButtonBase-root MuiListItem-root MuiMenuItem-root makeStyles-menuItem-350 MuiMenuItem-gutters MuiListItem-gutters MuiListItem-button
Unbelievable! This obviously poses a threat to readability and debugging. At least for me, there were situations when I feel trapped in a labyrinth of similar strings debugging using Chrome dev tools.
There are, however, some other libraries generating inline CSS on the fly. While this avoids problems with readability, it inevitably hinders the ability to use some common CSS features e.g. media queries and modifiers. There might be some way to get around it, but then you are adding another layer of complexity to your problem.
On the other hand, you can still avoid the problem with global scoping while use traditional CSS. Modularity solutions through Naming conventions like BEM or Pre-processors and Post-processors like LESS, SASS are all out there.
Encapsulation vs. Flexibility
One point the supporters of CSS-in-JS likes to mention is that the style and functionality becomes one unit, and when the function or even the application needs to be exported, the two are bundled together. This helps to achieve encapsulation through exposing only one API to the outside, and thus makes modifying and changing code less error-prone.
But on the other side, locking your CSS together with your JS makes your application less flexible. Since most CSS-in-JS libraries only fits well with component-based frameworks like React and Angular. (For example Styled-Components only works in React & Preact). Well, all is well when React and its alike are still in mass adoption, but it’s likely that there will be some other new tools coming in the future.
By coupling your CSS with a specific framework, you are losing the flexibility to transit your project into some other framework without massive refactoring on CSS — writing from scratch!
Advanced Functionality vs. Learning Curve & Error-prone
But the seemingly shinning life comes with a cost.
Secondly, with advanced functionality comes with more responsibility. You have to be more careful when write code, otherwise you will run into some bugs that you will not encounter previously in CSS.
In contrast, traditional CSS is error-tolerant. If a CSS rule is not supported on some browser, it will be simply ignored.
Minification vs. Other Kind of Optimisation
One thing that CSS-in-JS helps to improve performance is through minification of classnames.
But traditional CSS has its own approach (maybe better).
You are free to inline load the critical path CSS and make the rest of the CSS loading asynchronously.
Also to utilise the full power of CSS-in-JS, it’s recommended to use Server Side Rendering (SSR). But this adds extra requirement to your application which you might not need to think about it without it.
On the other side, for people who prefer traditional cascading style sheet, but would like to improve their CSS developer-experience, I believe tools like SASS are enough for you to take your CSS to the next level.