The CSS-in-React Landscape

Chris Coyier - Oct 21, 2021

I only half-jokingly refer to the CSS-in-JS world as CSS-in-React. Many of the libraries listed below theoretically work in non-React situations — they generally call that “framework-agnostic”) — but I’d guess the vast majority of their usage is on React projects. That’s because React, despite being a UI-focused library, has no particular blessed styling solution. Vue has style tags built right into Single File Components. Same with Svelte. Angular also has a built-in component-scoped styles solution. With React, it’s bring-your-own.

Perhaps not venturing too far out of their core strengths is a strength of React. I dunno. But you do have to make a choice on how to style things on your React projects. For example, you can simply write (and there is literally no problem with this), regular ol’ flat-file vanilla CSS to style your React projects. I’d recommend that over using inline style={{ }} on everything any day. But truth be told, there are some pretty nice advantages to choosing a library to help with styles. Things like:

  • Co-locating styles and components
  • Scoping styles to components
  • Using props in styling variations
  • Using JavaScript abilities within CSS syntax
  • Theming

Each library has its own set of fancy things that might be variations on the above, or might be totally unique to that library.

It’s also notable that by using a library where you author styles in your JavaScript, it’s not a 100% guarantee that you have to ship your styles in JavaScript. The libraries that use the term “zero runtime” are typically referring to the idea that the styles are compiled to CSS during a build process, so you use that CSS like any other, which is likely better for performance.

This research is brought to you by support from Frontend Masters, CSS-Tricks’ official learning partner.

Need front-end development training?

Frontend Masters is the best place to get it. They have courses on all the most important front-end technologies. Interested in leveling up your React skills? Here’s your best bet:

A couple of caveats before we go through the list:

  • I’m not deeply experienced in every single one of these libraries. I’ve used several of them on real projects, most heavily CSS Modules. I can’t quite speak to the nuances of each. The demos below are basic demonstrations of basic styntax.
  • If I get any facts wrong or you want to add more detail, hit me in the comments or via our contact form and I’ll improve things.
  • The point of this, partially, is to have a working code example of each for easy reference.

styled-components

  • Super popular — probably the most-used option.
  • Popularized the possibilities of dynamic styling and the magic of using props for variations.
  • Template literal syntax feels comfortably CSS-like. It seems like it really encourages using it that way, but it is possible (docs) to use Object syntax.
  • Supports SSR, but it’s not the same as “zero runtime” libraries (which means “compiles to static CSS.” It still ships a JavaScript runtime for any dynamic styles.

CSS Modules

  • Very simple. All it does is scope styles and encourage co-location of styles and components.
  • It’s fancy feature is composition, which is basically mixins based on existing classes.
  • Not a runtime thing at all — it requires a build process. But it still works with HMR and such. You can ship it with styles in JavaScript, or extract them into static CSS files. It does nothing dynamic, so it’s extremely “zero runtime” if you extract the CSS.
  • Can be combined with Sass.
  • Built into Next.js

Emotion

Emotion is a library designed for writing css styles with JavaScript. It provides powerful and predictable style composition in addition to a great developer experience with features such as source maps, labels, and testing utilities. Both string and object styles are supported.

  • It doesn’t seem terribly different than styled-components, TBQH, but this podcast gets into some of the performance differences.
  • Supports SSR, but is not zero-runtime.
  • Glamorous is totally deprecated (and I suppose Glam and Glamor as well?) in favor of Emotion, so that’s something.

Stitches

  • The Variants API is very useful and well done.
  • The TypeScript editor experience¹.
  • Supports theming and encourages using a design token approach.
  • Utilities allow you to build your own custom shorthands for styling.
  • Supports SSR — and it’s closer to zero-runtime, though not entirely. Also doesn’t seem to actually generate CSS files, rather it has a function that spits out CSS so you can use SSR via a <style> tag (which I can’t imagine is ideal for caching).
  • Here’s a Twitter thread with an honest review. Also see all the reactions to this.

vanilla-extract

  • I’d say vanilla-extract supports SSR but it’s more than that because that’s the only way it is used, unless you opt into specific “runtime” features, like dynamic theming. That’s the meaning of “zero runtime”: it only compiles to static CSS files.
  • The TypeScript editor experience¹. (Also see our recent article.)
  • Variants API, including a Recipes API that is like like the Stiches framework above.
  • Supports a theme and utility-class-like approach via Sprinkles
  • I was gonna put Aphrodite on this list, but the creators of it are moving to vanilla-extract, so it probably isn’t a great choice these days, even though it seems to do pretty much all the same stuff as these other libraries.

JSS

  • Has a React-specific integration
  • Has an extend syntax
  • Plugin architecture

Linaria

  • The OG of “Zero Runtime” CSS-in-JS libraries.
  • Compiles to actual CSS files, but still has a runtime if you do dynamic things (at least I think so).
  • Feels similar to styled-components API-wise.
  • Supports Critical CSS.

Styled JSX

  • Babel plugin, so definitely a build-process thing.
  • Using a <style jsx> tag right in the component at the level of nesting you want to scope to is a clever API.
  • Lack of nesting isn’t great — you have to repeat the selector name a lot.

Goober

  • Goober is notable because it has an awesome name and is a mere 1.25KB, which is like an order of magnitude smaller than anything else.
  • Pretty much the same feature set of styled-components and Emotion.

Interesting resources:

  • Shopify’s research on which library they wanted to switch to.
  • Facebook has something cooking (conference video), but hasn’t open-sourced anything. It’s apparently called “StyleX” — and there is already a library called “Style9” that attempts to match the features, including near-zero runtime, atomic CSS output, and the TypeScript experience.
  • If you’re into authoring in the atomic style, a lot of people think that using Tailwind (probably with just-in-time mode) is the way to go.
  • Probably a more React-y version of Tailwind is Styled System, which provides styles as a bunch of preconfigured props.
  • Twin is along the lines of authoring with atomic styles in a React-y way.
  • I could not get Compiled to work for me. I’m sure it was just me, but I gave up. It looks exactly like the styled-components API to me, except the output is atomic CSS classes, which does seem kinda cool.
  • The site CSS in JS Playground shows a bunch of examples, including a few libraries not mentioned here, like Fela, Radium, and more. My god, are there a lot of these things.
  1. By “the TypeScript editor experience,” I mean the library is written in TypeScript to take advantage of one of TypeScript’s best features: help autocompleting code in code editors. For example, if you’re using VS Code as your code editor, and you write a set of “color” variants, then type <Button color=" in your JSX file, you should get a list of your own compatible variants in the contextual VS Code autocomplete menu to choose from.