Comparing CSS in JS solutions

Tuesday, Mar 14 2017

After testing a few CSS in JS solution, I have found these criterias useful to help me decide which to pick:

  1. whether they generate inline styles or a separate stylesheet
  2. whether they require to define class names
  3. whether they supply a helper to create a new component from just a style definition

In a React application, defining styles with JavaScript allows you to store style definitions in the same place as your React components and easily modify the styles in response to props or user actions.

React has allowed a basic form of JavaScript styling for a long time. Since the talk that popularized the approach, a confusingly large number of complementary libraries has appeared.

Radium set out to solve the problem that features like pseudo-selectors (:hover) that do not work in inline styles. It emulates features like :hover by generating JavaScript code that simulates the effect of the CSS. Using Radium looks like this:

import React from 'react';
import Radium from 'radium';

function Button({ children }) {
  const styles = {
    backgroundColor: 'blue',
    color: 'white',
    border: 0,
  }
  return <button style={styles}>{children}</button>;
}

export default Radium(Button);

Radium outputs inline styles in the generated HTML. Intuitively, it feels this would inflate the page size and I was not too keen on emulating CSS properties with JavaScript event handlers, so I never tried it in earnest.

Aphrodite attempts to solve the problem of in a completely different way. Instead of creating JavaScript, it extracts the inline styles and generates CSS in a <style> element. Since in the generated HTML the style is not inline any more, you can use the full range of CSS. Besides Radium, all other tools follow Aphrodite’s approach of generating a separate stylesheet, so although you write styles inline in your components, the styles are not inline in the final HTML. Aphrodite’s particularity is that it requires you to define named rules:

import React from 'react';
import { StyleSheet, css } from 'aphrodite';

const styles = StyleSheet.create({
  button: {
    backgroundColor: 'blue',
    color: 'white',
    border: 0,
  }
})

function Button({ children }) {
  return <button className={css(styles.button)}>{children}</button>;
}

export default Button;

I tried Aphrodite first, but I felt named rules were superfluous. I wanted to colocate the styles with the markup because they define a visual and functional unit, so I would not be re-using the class names on different components.

It looks like Glamor attempted to solve this issue. It generates CSS just like Aphrodite, but you can pass CSS directly to your components and Glamor will generate a unique class name for that combination of styles.

The next generation of libraries exposes functions that generate components just from the style definitions. Here’s the button definition with Styled Components:

import styled from 'styled-components';

const Button = styled.button`
  background-color: blue;
  color: white;
  border: 0;
`;

export default Button;

What strikes you first is that it lets you write CSS directly in your JavaScript files (so you can write background-color instead of backgroundColor), but I don’t find that its most compelling feature. I get more confused when I have to switch between CSS and JavaScript. What’s interesting is that it speeds up the creation of presentational components, which are mostly concerned with look and feel. As a side-effect, you get an even better separation between behaviour and presentation.

Two libraries provide a similar experience to Styled Components, but with style definitions in JavaScript instead of CSS, Styletron and Fela.

Here’s an example using Styletron:

import { styled } from 'styletron-react';

const Button = styled('button', {
  backgroundColor: 'blue',
  color: 'white',
  border: 0,
});

export default Button;

Fela looks extremely similar. Fela has the advantage of supporting fonts and keyframes, but Styletron passes props to the underlying React element by default. For example, if I set an onClick handler on the button with Fela, I need to explicitly configure the component Fela generates to pass onClick to the <button> element, while Styletron does it automatically.

They also both attempt to generate atomic CSS, splitting re-used styles in their own classes. Since any reasonably designed web page is going to repeat property values, the goal here is to improve performance by generating a smaller stylesheet.

To summarize:

Utility Generated styles Language Class names Atomic
Radium Inline JS No No
Aphrodite Stylesheet JS Yes No
Glamor Stylesheet JS No No
Styled Components Stylesheet CSS No No
Fela Stylesheet JS No Yes
Styletron Stylesheet JS No Yes

For my book on React, I don’t plan to cover these libraries, but I hope to convey some of the lessons learned using them in the way I instruct the reader to build components.