Intro to CSS-in-JS: Generating CSS from JavaScript

What is CSS in JavaScript and why is it suddenly popular? Learn how it works and get introduced to some of the leading CSS-in-JS frameworks.

Building Blocks mason laying bricks

The idea of generating CSS in JavaScript has become more popular over the last few years, largely thanks to the dominance of reactive frameworks like React and Svelte. Such frameworks do not enforce using JavaScript to style components, but they lend themselves to it. Consequently, a number of CSS-in-JS libraries have come forward to make the process easier.

This article introduces you to CSS-in-JS, then showcases a handful of promising frameworks for implementing it.

What is CSS in JavaScript?

Old-school CSS has basically two options: inline definition and loading from an external file. In both cases, the browser loads the CSS, parses it, then applies the styles to the markup. CSS-in-JS offers a third approach: delivering CSS by programmatically generating it in code. 

The big positive here is that the JavaScript code has full access to the variables and conditions, including those representing the application state. Therefore, the CSS can be created as fully reactive to live context. The drawback is added complexity. This really is a tradeoff because one of the benefits of traditional CSS is simplicity, at least in terms of how styles are loaded. 

CSS-in-JS provides a syntax for turning your JavaScript into styles the browser can apply. Regardless of the framework you use, the result will look something like Listing 1.

Listing 1. CSS-in-JS with the styled-components framework


// Create a Title component that'll render an <h1> tag with some styles
const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

// Create a Wrapper component that'll render a <section> tag with some styles
const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

// Use Title and Wrapper like any other React component – except they're styled!
render(
  <Wrapper>
    <Title>
      Hello World!
    </Title>
  </Wrapper>
);


Listing 1 is taken from the styled-components framework. Each framework has its own conventions, but this example gives you the basic aspects of any system:

  1. Define CSS in JavaScript syntax.
  2. Apply the styles in the markup (like JSX).

Component-level CSS

Large-scale application styles are notoriously prone to bloat. It can be very challenging to understand what is influencing the characteristics of specific elements in a large layout, and even harder to make changes effectively. This brittleness makes maintaining CSS an onerous task at times.

CSS-in-JS addresses this problem with component-scoped CSS. Almost all JavaScript frameworks are component-oriented, so generating CSS that is scoped to those components is a natural fit.

By automatically ensuring the styles in a component are applied only to that component, the application developer is relieved of the need to devise globally unique classes to apply across the diversity of pages and layout sections. Component-level CSS means the way a layout is composed naturally informs how the CSS styles are applied.

Of course, applications still need to be able to apply styles and inherit from them. Any CSS-in-JS framework worth its salt must address that concern.

Single-page vs. multi-page applications

Lately, there’s been much ado about single-page applications versus multi-page applications. Particularly, there are questions about which parts of an application can be fully dynamic, which can be pre-rendered, and which require a bit of both. The bottom line for CSS-in-JS is that styles must be generated wherever they are necessary; be that on the server or on the client. Fortunately, that seems to be the case for most frameworks.

CSS-in-JS frameworks

The first thing you find when considering a CSS-in-JS implementation is the number of available options. Your best choice will be informed, first and foremost, by the JavaScript framework you are using. Some CSS-in-JS solutions are specific to a particular reactive framework, while others are agnostic. Even among framework-agnostic CSS-in-JS libraries, there is often an affinity for a particular framework. Therefore, it's worth considering which CSS-in-JS solution is popular within the community that supports the framework you are using.

Another feature to consider when evaluating frameworks is support for TypeScript. Not all CSS-in-JS frameworks work with TypeScript, although it's becoming more the norm.

Let's take a look at some of the better frameworks available.

Styled-components

Styled-components is one of the longest-lived CSS-in-JS frameworks. It's geared to React (although there are efforts to use it elsewhere) and primarily concerned with styling React components. It is quite active and popular, with over 37,000 stars on GitHub. 

You saw an example of styled components in Listing 1.

Emotion

Emotion is framework-agnostic, although it seems to have an affinity for Svelte. Listing 2 has a sample of Emotion. In the sample, notice that we are looking at an inline CSS definition using JavaScript syntax.

Listing 2. Emotion inline CSS-in-JS


import { css, cx } from '@emotion/css'

const color = 'white'

render(
  <div
    className={css`
      padding: 32px;
      background-color: hotpink;
      font-size: 24px;
      border-radius: 4px;
      &:hover {
        color: ${color};
      }
    `}
  >
    Hover to change color.
  </div>
)

Styled JSX

Styled JSX is the default CSS-in-JS solution for Next.js, which unfortunately lends it a certain inertia. It’s a healthy Git project, with over 7,000 stars, but it isn’t as active as some of the other projects described here (it has a v2 branch that seems to have gone dormant). 

Styled JSX is an obvious choice when you are using Next.js, but it is possible to swap in a different React-friendly CSS-in-JS library if you wish.

CSS modules

CSS modules is an early and influential implementation of the CSS-in-JS idea. The project on GitHub has over 16,000 stars, but hasn’t been updated in several years. It is framework-agnostic and can be integrated into many popular reactive libraries. For example, here it is with Vue.

CSS modules is intended to be a general solution that works outside of a framework component system, to make locally scoped styles by default. Note that although CSS modules sounds like an official specification, it really isn’t—it’s a project with a specific take on how to achieve CSS-in-JS.

Twin

Tailwind CSS is a functional CSS library. It is something of a darling among JavaScript developers, so it’s inevitable that it would be united with a CSS-in-JS approach. Twin combines Tailwind with CSS-in-JS. 

Twin lets us use Tailwind’s classes in several CSS-in-JS implementations, as described here. It is an active and growing project, with more than 6,000 stars on GitHub.

Twin has various examples of how to incorporate it with a variety of frameworks and build tools. For example, here's how it can be combined with Emotion via Webpack.

JSS

JSS is a framework-agnostic approach with over 6,000 GitHub stars. It seems to be quite popular and has good documentation, though it hasn’t seen much activity in the repository lately. JSS is one of the oldest active CSS-in-JS solutions and is in some ways the progenitor of the movement.

Angular

Angular, like many Reactive frameworks, supports component-level CSS. Angular’s system is fairly powerful and flexible, with similar features to the other libraries. This fits with Angular’s all-in-one design philosophy, and it seems to be the most common approach when using Angular. It is possible, however, to use a CSS-in-JS framework like JSS.

Drawbacks of using CSS in JavaScript

Although CSS-in-JS is very popular, there is a counter-trend against it. The reasons boil down to performance and complexity. A recent article by Sam Magura, an active maintainer of the Emotion framework, describes the issues in detail. The main performance issue is that CSS-in-JS turns CSS into a runtime consideration, which increases the work the browser and framework do at runtime. The result is slower load times and more code that can break.

But the article is also clear about the benefits to CSS-in-JS, which I have covered in this article. So, the solution is not to reject CSS-in-JS but find a way to get the benefits while minimizing the drawbacks. The article discusses a variety of possible workarounds to CSS-in-JS performance challenges.

Like everything in software, the community keeps pushing forward for better ideas. Now, we are looking for ways to keep the benefits of CSS-in-JS while minimizing the downsides.

Conclusion

Using a CSS-in-JS framework isn't always necessary, but it can offer major benefits over using straight CSS or CSS preprocessor alone. With a variety of solutions to choose from, it should be possible to find one that fits your favored stack. Moreover, you are likely to encounter these frameworks on existing projects, so knowing what they are and how they work is beneficial.

Copyright © 2022 IDG Communications, Inc.

InfoWorld Technology of the Year Awards 2023. Now open for entries!