An Introduction to CSS-in-JS: Examples, Pros, and Cons

CSS-in-JS libraries provide us with a new approach for writing CSS. They aim to tackle the limitations of CSS, such as the lack of dynamic functionality, scoping, and portability. In spite of its advantages, CSS-in-JS is a controversial technology, as many developers ask if it’s worth further complicating front-end development. 

This article intends to give you a high-level overview of CSS-in-JS libraries. We’ll look into how these libraries work, which problems they solve, and how to decide if you should use them.

What Is CSS-in-JS?

CSS-in-JS libraries have been gaining traction since component-based JavaScript frameworks appeared in front-end development. Angular, React, Vue, and other frameworks are all based on modules called “components” from which you can build up an entire single-page application (SPA). A component is usually a UI element such as a button, pop-up, or navigation bar. You only need to create a component once and you can reuse it in any context within the application. 

But, how should you style SPA components efficiently? If you use global CSS files then it will be hard to tell what the end result will look like. Due to the cascading nature of CSS (Cascading Style Sheets), stylesheets can load in any order and override each other in any combination. Managing dependencies is another problem when it comes to styling SPAs. If dependency management is difficult when working with regular websites, it’ll be almost impossible with a modular web app above a certain complexity.

A well-designed CSS-in-JS library can solve all of these problems. CSS-in-JS is, in fact, a JavaScript library that bundles each JavaScript component with all its belonging CSS rules and dependencies. As a result, components can run independently, without relying on any external CSS file. This is how Max Stoiber, the co-creator of the Styled Components library explains in his excellent blog post how CSS-in-JS libraries work:

“For three years, I have styled my web apps without any .css files. Instead, I have written all the CSS in JavaScript. … I can add, change and delete CSS without any unexpected consequences. My
changes to the styling of a component will not affect anything else. If
I delete a component, I delete its CSS too. No more append-only stylesheets!” – Max Stoiber

I think this statement summarizes the technique pretty well. With CSS-in-JS libraries, you write all your CSS within .js files so that they can work as JavaScript modules.

Examples of CSS-in-JS Libraries

Although every CSS-in-JS library serves the same purpose (i.e. bundling components with CSS), each comes with slightly different features and syntax. Let’s see how the most popular CSS-in-JS libraries stack up to each other.

Framework-Specific vs. Framework-Agnostic

Some libraries only work with a specific JavaScript framework. For example, Radium has been created for React apps, while Styled JSX only supports components written in JSX (that you can use both with and without React). 

Framework-specific CSS-in-JS libraries use the same syntax as the framework they support. For instance, Styled JSX uses template literals within the JSX syntax to add CSS styles to components. The following code (from Styled JSX’s GitHub docs) creates two types of buttons, a regular and a large one:

Other CSS-in-JS libraries such as JSS, Emotion, and the aforementioned Styled Components are framework-agnostic. So, you can use them together with any component-based frameworks, plain JavaScript, and some of them such as Aphrodite work with Web Components as well.

JSS css-in-js

To see how framework-agnostic CSS-in-JS libraries look in real life, you can try JSS’ online playgrounds or check out the following Web Component example from Aphrodite’s GitHub docs:

Unique Selectors vs. Inline Styles

To handle scoping, most CSS-in-JS libraries such as JSS, Emotion, and Styled Components automatically generate a unique selector for each component. To see it for yourself, you can go to Styled Component’s homepage and inspect one of the examples with your browser’s developer tools. For example, I’ve inspected the pink-ish "I'm a styled <Button />" ghost button with Firefox DevTools:

Styled Component unique selector css in js

As you can see, Styled Components has generated two unique classes, one called components__SecondButton-sc-1gmolv7-3 fWxBIM for the button and another one called components__AlignCenter-sc-1gmolv7-0 hjdyfl for the parent div.

Although this is a great way to create absolutely unique selectors that won’t cause any CSS specificity problems, the source code doesn’t look very pretty. Alternatively, some CSS-in-JS libraries such as Radium add inline CSS on the fly to the HTML instead of generating unique classes.

The latter approach doesn’t only lead to more readable HTML code but has other advantages as well, such as better performance, source order independence, and dead code elimination. However, using inline styles has some shortcomings, too. According to Radium’s documentation:

“Despite that, there are some common CSS features and techniques that
inline styles don’t easily accommodate: media queries, browser states
(:hover, :focus, :active) and modifiers (no more .btn-primary!). Radium
offers a standard interface and abstractions for dealing with these
problems.”

So, that’s the trade-off. Based on your needs, you can decide which is the better approach for you.

Unique Features

CSS-in-JS libraries don’t only differ in syntax, framework support, and scope handling, but each has unique capabilities that are not necessarily included in other libraries, such as:

  • global selectors,
  • state-based styling,
  • client vs. server-side rendering (or both),
  • caching,
  • source maps,
  • built-in auto-prefixing,
  • media queries,
  • selector nesting
  • built-in support for animations,
  • additional plugins and packages,
  • and others.

Although most libraries come with good documentation, here’s a comparison table by Michele Bertoli that includes the most important features of multiple CSS-in-JS libraries.

Michele Bertolis table of CSS-in-JS libraries
Michele Bertoli’s table of CSS-in-JS libraries (it’s a big one!)

Advantages of CSS-in-JS

Now, let’s talk about the advantages of CSS-in-JS libraries (even though I’ve already mentioned some of them).

Local Scoping

By default, CSS doesn’t allow local scoping. Each style rule has a global scope, so it applies to the entire project. As a result, styles can override each other in surprising ways. Front-end developers have created multiple methodologies that add modularity to CSS, such as BEM, OOCSS, and SMACSS. Pre-processors and PostCSS are another way to modularize CSS. CSS-in-JS libraries take this concept to the next level by automating scoping, which leads to a high level of predictability.

Encapsulation

Encapsulated components hide the details of implementation. They only expose an API to the outside world so that other components can interact with them. Encapsulation facilitates maintenance and eliminates errors, as you can modify all component-related code in the same place, without having to worry about unexpectedly changing other parts of the application. 

Portability

As components include all the source code, styles, and logic they need for proper running, you can securely move them around. They basically work out of the box and let you create loosely-coupled apps in which components communicate with each other solely via APIs.

Reusability

Components are reusable, so you only have to write them once, then you can run them everywhere. You can’t only reuse components within the same application but also in other apps built with the same framework.

Dynamic Functionality

As CSS-in-JS is essentially JavaScript code, you can apply complex logic to your style rules, such as loops, conditionals, variables, state-based styling, and more. Therefore, it’s an ideal solution if you need to create a complicated UI that requires dynamic functionality.

Disadvantages of CSS-in-JS

Although CSS-in-JS libraries allow you to build component-based applications in a logical and efficient manner, they also have some characteristics that might make you wary of them.

Learning Curve

CSS-in-JS definitely has a learning curve, especially if you have used neither component-based frameworks nor web components before. Besides learning the new syntax, you also need to pick up a new way of thinking, which needs time and might slow down your development workflow for a while.

Extra Layer of Complexity

Putting a CSS-in-JS library into use adds an extra layer to your front-end stack, which can sometimes be unnecessary. Although CSS-in-JS manages complexity excellently, it’s not always worth the hassle, especially in the case of a simpler project.

Code Readability

Automatically generated selectors significantly worsen code readability. This can be a huge concern for you if you regularly use your browser’s developer tools for debugging, but also for beginners, as it’s essential for them to understand the code they wrote.

So, When Should You Use CSS-in-JS Libraries?

CSS-in-JS libraries provide you with a straightforward and secure methodology to style component-based applications. Besides JavaScript frameworks, you can also use them together with Web Components. They can also come in handy if you want to create a complex UX for a monolithic front-end, such as state-based functionality.

On the other hand, CSS-in-JS is probably not for you if you are a beginner developer, want to create websites without a dynamic front-end, or appreciate simplicity and code readability. You might reasonably find that your go-to tools such as Sass or PostCSS are perfect for your goals and that you don’t want to pick up a new technology just for the sake of novelty.

Learn More About CSS-in-JS and Web Components

However you decide, CSS-in-JS will most likely further gain traction in front-end development. If you are interested in the in-depth working of these libraries, check out the articles of Oleg Isonen, the creator of the JSS framework about “What actually is CSS-in-JS?” and “The tradeoffs of CSS-in-JS”, too.