What is it? Please use an analogy
But what about CSS?
For a long time, CSS had used extensions such as SCSS to give itself more power (variables, mixins, extends, etc.), and methodologies like BEM (
It’s often too time-consuming to remove the unused CSS—as it’s often difficult to know where the CSS was being used. The problem with Cascading Style Sheets is that they Cascade.
Problems with SCSS
Compared to the vanilla CSS of yesteryear, SCSS felt superpowered. It allowed developers to use more programmatic approaches to maintaining the code. They could use variables, functions (mixins), operators and nesting (things which modern vanilla CSS mostly provides natively, btw). But as the saying goes: “With great power comes a greater way of making things difficult for yourself.”
- CSS bloat: mixins provide a convenient way to generate code duplication
- Nesting abuse: nesting can be overused and makes it difficult to understand the context of the CSS you’re writing
- Extends abuse: misuse of extends can lead to creating huuuge comma separated class lists
- Compile-time uncertainty: SCSS converts to browser-readable CSS during application compilation, during which time it can be difficult to know in which order CSS will get… ordered, causing specificity problems
- Manual importing: developers need to know which SCSS files to
@importfor external references within a SCSS file to work—else expect node-sass compilation errors
- Lack of tree-shaking: SCSS’s
@importmakes all items global, making it difficult for compilers to know where a section of CSS exists. Each stylesheet is executed and its CSS emitted every time it is
@import-ed, which increases compilation time and produces bloated output
- Lack of IntelliSense and traversability: with modern tools like TypeScript providing immediate information about each section of code (the type of values a variable holds, the arguments a function takes, etc.), SCSS provides little without additional plugins and configuration
Problems with BEM
block__element--modifier) exists to provide context to the developers to understand the element’s place in the HTML hierarchy. It was created as a way of better linking HTML/CSS assets but is prone to developer interpretation which leads to convoluted namings, manageability issues and eventual collapse.
- Brittle naming: providing unique, descriptive names to classes can be difficult due to similar components that exist or may exist in the future
- Long names are long: class names can get extremely long, and given there’s no upper limit to the amount that can be used, it can take up large amounts of space both in the HTML structure and the corresponding CSS classes, particularly with SCSS usage
- Multiple levels: components with levels of depth of more than parent > child leads to a break in naming conventions (i.e.
.parent__grandchild) Multiple classes - in complex applications, there’s often need for multiple BEM classes on a single element, increasing the size of the CSS & HTML output further
The case for CSS-in-JS
Tighter, locally scoped CSS coupling
Reduced CSS overhead
As styles are bundled with the component, if the component itself is not used, the CSS is not included, creating painless CSS maintenance.
No more worrying about BEM naming
As styles are generated client-side and respond to the HTML rendering, CSS class names are auto-generated, saving developer time worrying about unique CSS naming as well as confusion if a CSS class no longer reflects the element it covers.
Theming and Context
In the case of React, developers can pass Context-ual information down through multiple levels of the component tree. This means global styles can be passed to each component without any manual imports from developers. I.e. padding, colours, font names or any text-based data can have one source of truth which requires zero work to access across the application.
IntelliSense & AutoComplete
CSS provides no immediate feedback that the mixin or variable they’re referencing exists. With CSS-in-JS (particularly in TypeScript) the developer immediately knows if they’re using the reference correctly, and can also AutoComplete CSS properties (as well as custom properties if they’ve been pre-typed within the Theme context).
Portability & Reusability
Reduced cognitive load
Easier SSR of styles
Server-side rendering is paramount for the SEO score of single-page applications, and most CSS-in-JS solutions provide a clean, reliable approach to achieving this.
As CSS-in-JS has been a consideration for applications for a while now, there are multiple widely used, mature options to choose from. These options are currently available for React applications but may also have ports to support other frameworks.
- styled-components: easily write CSS-in-JS using much of the same syntax that developers are familiar with coming from a vanilla CSS or SCSS background. Currently the most popular solution
- Aphrodite: a platform-agnostic CSS-in-JS solution, though not as widely used as the above
There are many more CSS-in-JS to consider for yourself here. For the examples below, I’ll be using styled-components.
So how do you use styled-components?
The main difference is understanding that the CSS you write will be coupled to a component, rather than being able to be applied to different components. Although styled-components does provide an API to reuse chunks of CSS.
How does it compare with regular HTML/CSS?
Here’s a quick interactive comparison in CodeSandbox:
Cons of styled-components
Like any solution to complex problems, it’s not without a few drawbacks.
Change from traditional CSS
Harder to locate local CSS
If class names are automatically generated, they won’t relate to the code the developer is looking at locally, making it harder to associate where CSS will live (though there are different solutions for this).
Coupling styles with a UI framework
In the example of styled-components, it is focused on React applications, meaning those same styles are not as easily shared with non-React applications. Though using an agnostic CSS primer like design tokens would mean that regardless of the framework or CSS-in-JS option, they would consume the same core styles.
For organisations already invested in frameworks such as React, it makes sense to harness that declarative flexibility to drive both HTML and CSS rendering, reducing the load on developers and speeding up the user experience.