Esteban Herrera Family man, #Java and #Javascript developer. #Swift, and #VR/#AR hobbyist. Like #books, #movies and still trying many things. eherrera.net

The best React inline style libraries  compared

We put Radium, Aphrodite, and Emotion to the test

11 min read 3167

The Best React Inline Style Libraries Compared

In any non-trivial React app, CSS styles can become a problem if you don’t manage them correctly.

Global style definitions, !important rules everywhere, and low flexibility when reusing components are examples of these problems.

This has led to alternative approaches to the classical approach of using CSS files. For example, here’s a good post about three styling approaches.

This article is about inline styles. However, I’m not going to talk about what they are or whether you should use them or not.

I’m going to talk about libraries that will help you use inline styles in your React application — libraries that allow you to use features that are not directly supported otherwise (like media queries).

I’ll compare:

This list is the result of not considering libraries that:

  • Don’t seem to be maintained anymore
  • Don’t seem to be popular
  • Don’t work with inline styles as object or string literals

However, as with most articles of this type, you may not agree with this list so feel free to post a comment with your favorite library and what’s special about it.

Using each of those libraries, I’m going to style a div element with the following CSS rules:

background-color: #00ffff;
text-align: center;
width: 100%;
padding: 20px;
:hover {
  color: #ffffff;
  cursor: 'pointer'
}
@media (max-width: 700px) { 
  background-color: #ff0000;
}

This will be the result:

At the end of this article, you’ll find a table summarizing the features of the libraries.

Let’s start with Radium.

Radium

Of the three libraries presented here, Radium is the most popular one in terms of GitHub stars, issues, and StackOverflow questions.

If you already have some inline styles as object literals, you don’t have to modify them to use Radium.

For example, here’s the style object that corresponds to the CSS rules shown earlier:

const styles = {
  panel: {
    backgroundColor: '#00ffff',
    textAlign: 'center',
    width: '100%',
    padding: '20px',
    ':hover': {
      color: '#ffffff',
      cursor: 'pointer'
    },
    '@media (max-width: 700px)': {
      backgroundColor: '#ff0000'
    }
  }
};

It looks like a regular inline style, except for the hover and media rules, which are not supported by common inline styles.

If you apply this style to a component:

class App extends Component {
  render() {
    return (
      <div style={styles.panel}>
        React rocks!
      </div>
    );
  }
}

And run the app, the div element will change its style but it will ignore the hover and media rules. Actually, this warning will be shown in the console:

Warning: Unsupported style property @media (max-width: 700px). Did you mean @media (maxWidth: 700px)?

To use Radium, you first have to install it:

npm install --save radium

And then import or require it:

import Radium from 'radium';
// Or const Radium = require('radium');

Radium is a higher-order component (HOC).

It processes the style attribute of the components specified in the render method, adding handlers that update the state if interactive styles (like hover), applying vendor prefixes, and merging styles among other things.

You can use it this way:

class App extends Component {
// ...
}
export default Radium(App);
// Or App = Radium(App);

Or with ES7 decorators:

@Radium
class App extends Component {
// ...
}

Usually that will be enough, but if you’re using media queries, keyframes, or some Radium plugins, you also need to use the StyleRoot component to wrap the top-level component of your app:

ReactDOM.render(
    <StyleRoot><App /></StyleRoot>,
    document.getElementById('root')
);

Now if you run the application, and inspect the div element, you’ll see this:

<div id="root">
    <div data-radium="true">
        <div class="rmq-f7d82907" data-radium="true"
              style="background-color: rgb(0, 255, 255); text-align: center; width: 100%; padding: 20px;">
            React + Radium rocks!
        </div>
        <style>
            @media (max-width: 700px){ 
                 .rmq-f7d82907{ background-color: #ff0000 !important; }
            }
        </style>
    </div>
</div>

Notice that for the media query, it created a new CSS class with a random name within a style element. According to the documentation, it does it this way so media queries work correctly with server-side rendering.

And what about the hover effect?

When you mouse over the element, its style attribute is updated to add the specified hover style:

You can also specify an array of styles in the style attribute. This comes in handy when you need to override some styles depending on the value of a property, for example:

const styles = {
  panel: {
    ...
  },
  alert: {
     backgroundColor: 'red'
  }
};

class App extends Component {
  render() {
    return (
      <div style={[
            styles.panel,
            this.props.alert && styles.alert
        ]}
      >
        React + Radium rocks!
      </div>
    );
  }
}

There’s also a Style component that renders a style element, which, for example, helps you add CSS rules to the body element or scope classes applied to other elements.

For instance, this:

<div className="local-scope" style={styles.panel}>
    React + Radium <span>rocks!</span>
      
    <Style
        scopeSelector=".local-scope"
        rules={{
            fontWeight: 'bold',
            span: {
              textTransform: 'uppercase'
          }
        }}
    />
    <Style rules={{
          body: {
              backgroundColor: 'black'
          }
        }}
    />
</div>

Will render:

<div class="rmq-f7d82907 local-scope" data-radium="true" style="background-color: rgb(0, 255, 255); text-align: center; width: 100%; padding: 20px;">
    React + Radium <span>rocks!</span>

    <style>
        .local-scope {
            font-weight: bold;
        }
        .local-scope span {
            text-transform: uppercase;
        }
    </style>
    <style>
        body {
            background-color: black;
        }
    </style>
</div>

On the other hand, one disadvantage of Radium is that only three states are supported: :hover:foucs, and :active, and if you have more than one element in your component that uses one of these states, you need to provide a unique key prop.

There’s even a function that allows you to query these states, getState:

<div style={styles.panel}>
React + Radium rocks!
{ Radium.getState(this.state, null, ':hover') ? (
<span>Yeah!</span>
    )
    : null
  }
</div>

However, this means that you have to manually implement pseudo-selectors like :checked:last:before, or :after.

Another missing functionality is the support of @font-face. However, Radium implements almost all of its functionality as a plugin and you can also use the plugin API to implement custom functionality, like @font-faces.

A Radium plugin is a function that accepts a PluginConfig object and returns a PluginResult object and it is called once for every rendered element that has a style attribute.

Here you can take a look at the source code of the plugins include in Radium. And in this repository by Ian Obermiller, you can find another example of a plugin.

Here’s the basic example implemented with Radium:

Aphrodite

Aphrodite is another popular library for writing CSS in JavaScript, but it takes a slightly different approach than Radium.

First, install it with:

npm install --save aphrodite

Once again, you have an object with the styles of the application:

{
  panel: {
    backgroundColor: '#00ffff',
    textAlign: 'center',
    width: '100%',
    padding: '20px',
    ':hover': {
      color: '#ffffff',
      cursor: 'pointer'
    },
    '@media (max-width: 700px)': {
      backgroundColor: '#ff0000'
    }
  }
}

But this time, you have to pass this object to the function StyleSheet.create:

const styles = StyleSheet.create({
  panel: {
    backgroundColor: '#00ffff',
    textAlign: 'center',
    width: '100%',
    padding: '20px',
    ':hover': {
      color: '#ffffff',
      cursor: 'pointer'
    },
    '@media (max-width: 700px)': {
      backgroundColor: '#ff0000'
    }
  }
});

In turn, the styles from this object are passed to the css function, and the result is used in the className property of the component:

import { StyleSheet, css } from 'aphrodite';

const styles = //...

class App extends Component {
  render() {
    return (
      <div className={css(styles.panel)}>
        React + Aphrodite rocks!
      </div>
    );
  }
}

Notice that Aphrodite uses the className property, not the style property.

To understand this, if you print the result of css(styles.panel) you’ll get a class name. In my case I got panel_w3twfb.

If you take a look at the object returned by StyleSheet.create, you’ll see something like:

{
  panel: {
    _len:188,
    _name:"panel_w3twfb",
    _definition: {
      backgroundColor:"#00ffff",
      textAlign:"center",
      width:"100%",
      padding:"20px",
      ":hover": {
        color:"#ffffff",
        cursor:"pointer"
      },
      "@media (max-width: 700px)": {
        backgroundColor:"#ff0000"
      }
    }
  }
}

That function returned an object that wrapped the CSS rules and added a _name property, with the same value as the one returned by the css function.

Using the Inspector tool of your browser, you’ll see the definition of this panel_w3twfb class:

.panel_w3twfb {
background-color: rgb(0, 255, 255) !important;
text-align: center !important;
width: 100% !important;
padding: 20px !important;
}

By default, Aphrodite appends !important to all the CSS rules. If you don’t want this, the only thing you need to do is import aphroidte/no-important instead of aphrodite:

import { StyleSheet, css } from 'aphrodite/no-important';

Also, when the flag NODE_ENV is set to production or if you call minify(true) before StyleSheet.create:

import { StyleSheet, css, minify } from 'aphrodite';
minify(true);
const styles = StyleSheet.create({
//...
});

Aphrodite will only keep the hash in the name of the CSS class. In this case, w3twfb.

But where’s this class? It’s not defined next to the div element or anywhere else in the body of the document.

Aphrodite creates a style element inside the document’s <head> element to put its generated styles:

<html lang="en">
  <head>
    ...
    <style type="text/css" data-aphrodite=""></style>
  </head>
  <body>
    ...
  </body>
</html>

However, you can create a style element with the data-aphrodite attribute, and Aphrodite will use it instead of creating one.

Aphrodite will buffer writes to the style to avoid many DOM modifications. If you calculate the element’s dimensions in componentDidMount or componentDidUpdate, the documentation recommends using setTimeout or flushToStyleTag to ensure all styles are injected correctly.

And what about the hover style and the media query?

They are also added:

.panel_w3twfb:hover {
  color: rgb(255, 255, 255) !important;
  cursor: pointer !important;
}
@media (max-width: 700px) {
  .panel_w3twfb {
    background-color: rgb(255, 0, 0) !important;
  }
}

In a similar way than Radium, you can combine more than one style:

const styles = StyleSheet.create({
  panel: {
    ...
  },
  alert: {
     backgroundColor: 'red'
  }
});

class App extends Component {
  render() {
    return (
      <div style={css(
            styles.panel,
            this.props.alert && styles.alert
        )}
      >
        React + Aphrodite rocks!
      </div>
    );
  }
}

Also like Radium, it has limited support for nesting. For instance, this issueshows an example of a Saas-like class for an element that has both classes, jump-btn and disabled:

.jump-btn {
  width: 32px;
  height: 32px;
  background: url('jump_btn.png') no-repeat 0 0;
  &.disabled {
    background-position: -32px 0;
  }
  ...
}

That has to be written in Aphrodite in the following way because only pseudo selectors and media queries can be nested:

const styles = StyleSheet.create({
  jumpBtn: {
    width: 32,
    height: 32,
    background: 'background: url('jump_btn.png') no-repeat 0 0',
  },
  disabled: {
    backgroundPosition: '-32px 0',
  }
});
const classes = css(styles.jumpBtn, isDisabled && styles.disabled);

However, unlike Radium, Aphrodite support font faces:

const raleway = {
    fontFamily: "Raleway",
    fontStyle: "normal",
    fontWeight: "normal",
    src: "local('Raleway'), local('Raleway-Regular'), url(https://fonts.gstatic.com/s/raleway/v12/1Ptug8zYS_SKggPNyCMIT5lu.woff2) format('woff2')"
};
const styles = StyleSheet.create({
  panel: {
    fontFamily: [raleway, 'sans-serif']
    ...
  }
});

After and before pseudo-elements natively (with the caveat that the content property requires double or single quotes inside the string value):

const styles = StyleSheet.create({
  panel: {
    ...
    ':after': {
        content: '" Aphrodite!"',
    }
});

On the other hand, Aphrodite doesn’t provide an API the way Radium does. Granted, it works in a different way, but it may be helpful for some situations (for example, see here and here).

It provides an extension mechanism, but currently, it only allows you to generate new selectors based on the specified styles (it’s used by the library to handle media queries and pseudo elements/classes).

Here you can see the basic example implemented with Aphrodite:

Emotion

Recently, Kent C. Dodds deprecated Glamorous (a library that otherwise would have made into this list) in favor of EmotionHis reasons:

  1. Emotion can do everything Glamorous can do
  2. Emotion can do more than Glamorous can do
  3. Emotion is faster than Glamorous
  4. Emotion is smaller than Glamorous

That tells us something about this library, right?

First, install it with:

npm install --save emotion

One more time, starting with the object that contains the styles of the application:

{
  panel: {
    backgroundColor: '#00ffff',
    textAlign: 'center',
    width: '100%',
    padding: '20px',
    ':hover': {
      color: '#ffffff',
      cursor: 'pointer'
    },
    '@media (max-width: 700px)': {
      backgroundColor: '#ff0000'
    }
  }
}

You only need to modify the format of the :hover pseudo class. From:

':hover': {
  ...
}

To:

'&:hover': {
  ...
}

Emotion is similar to Aphrodite. Both use the className property and a function called css:

import { css } from 'emotion';

const styles = {
  panel: {
    // ...
  }
};

class App extends Component {
  render() {
    return (
      <div className={css(styles.panel)}>
        React + Emotion rocks!
      </div>
    );
  }
}

The css function returns the name of the CSS class that is generated automatically. In my case, it returned css-4k75yl.

The rendered HTML looks like this:

<div class="css-4k75yl">
  React + Emotion rocks!
</div>

And you can find the definition of this class inside the document’s <head> element:

<style data-emotion="">
  .css-4k75yl {
      background-color:#00ffff;
      text-align:center;
      width:100%;
      padding:20px;
  }
</style>
<style data-emotion="">
  .css-4k75yl:hover {
      color:#ffffff;cursor:pointer;
  }
</style>
<style data-emotion="">
  @media (max-width:700px) {
      .css-4k75yl {
          background-color:#ff0000;
      }
  }
</style>

Optionally, you can also add a label property to the style object to append a custom name to the generated CSS class.

For example, the following definition:

const styles = {
  panel: {
    backgroundColor: '#00ffff',
    textAlign: 'center',
    width: '100%',
    padding: '20px',
    '&:hover': {
      color: '#ffffff',
      cursor: 'pointer'
    },
    '@media (max-width: 700px)': {
      backgroundColor: '#ff0000'
    },
    label: 'my-name'
  }
};

Will result in the class name css-4k75yl-my-name.

So in this way, Emotion is not that different from Aphrodite:

But something about this library is that it has way more features than Radium and Aphrodite.

For example, with Emotion you have two more ways to style components.

Instead of an object literal, you specify the style as a tagged template:

const style = css`
  background-color: #00ffff;
  text-align: center;
  width: 100%;
  padding 20px;
  &:hover {
    color: #ffffff;
    cursor: pointer;
  }
  @media (max-width: 700px) {
    background-color: #ff0000;
  }
`;

class App extends React.Component {
  render() {
    return (
      <div className={style}>
        React + Emotion rocks!
      </div>
    );
  }
}

Notice that the syntax is completely different. It’s more like CSS:

  • The names of the rules are not camel-cased
  • Quotes are not used
  • Rules are separated with a semicolon

On the other hand, you can also style elements or components with the styled function.

For this, first, you need to install react-emotion (or preact-emotion if you’re using Preact):

npm install --save react-emotion

Then, calling the function by first passing an HTML tag or React/Preact component and then either an object literal with the styles:

import styled from 'react-emotion';

const MyDiv = styled('div')({
    backgroundColor: '#00ffff',
    textAlign: 'center',
    width: '100%',
    padding: '20px',
    '&:hover': {
      color: '#ffffff',
      cursor: 'pointer'
    },
    '@media (max-width: 700px)': {
      backgroundColor: '#ff0000'
    }
  }
);

Or a template literal:

const MyDiv = styled('div')`
  background-color: #00ffff;
  text-align: center;
  width: 100%;
  padding 20px;
  &:hover {
    color: #ffffff;
    cursor: pointer;
  }
  @media (max-width: 700px) {
    background-color: #ff0000;
  }
`;

And use that new styled component like any other:

class App extends Component {
  render() {
    return (
      <MyDiv>
        React + Emotion rocks!
      </MyDiv>
    );
  }
}

Of course, props can be passed to this component and you can change its styles of based on the props:

const MyDiv = styled('div')(props => ({
    backgroundColor: props.bc,
    textAlign: 'center',
    width: '100%',
    padding: '20px',
    '&:hover': {
      color: '#ffffff',
      cursor: 'pointer'
    },
    '@media (max-width: 700px)': {
      backgroundColor: '#ff0000'
    }
}));

class App extends Component {
  render() {
    return (
      <MyDiv bc='#00ffff'>
        React + Emotion rocks!
      </MyDiv>
    );
  }
}

In the documentation page for styled components, you can check out more configuration options.

You can also combine styles:

const styles = {
  panel: {
    ...
  },
  alert: {
     backgroundColor: 'red'
  }
};

class App extends Component {
  render() {
    return (
      <div style={css(
            styles.panel,
            this.props.alert && styles.alert
        )}
      >
        React + Emotion rocks!
      </div>
    );
  }
}

And Emotion will merge them in the order they appear (notice the class name changes):

.css-17nr31q {
  background-color: #00ffff;
  text-align: center;
  width: 100%;
  padding: 20px;
  background-color: red;
}

However, unlike Aphrodite, when combining multiple class names, Emotion provides some advanced options.

Also unlike Aphrodite (but similar to Radium), Emotion allows you to specify global styles easily:

import { injectGlobal } from 'emotion'

injectGlobal`
  body {
    background-color: black;
  }
`
// Or injectGlobal({ body: { backgroundColor: 'black' } });
view raw

Emotion supports server-side rendering and keyframes like the other libraries, but it has better support for nested selectors and something unique is the support for themes, provided by the library emotion-theming.

For example, after installing emotion-theming with:

npm install --save emotion-theming

You can put the background color style in a theme to share it across other components:

import styled from 'react-emotion';
import { ThemeProvider } from 'emotion-theming';

const theme = {
  backgroundColor: '#00ffff'
};

const MyDiv = styled('div')(props => ({
  backgroundColor: props.theme.backgroundColor,
  textAlign: 'center',
  width: '100%',
  padding: '20px',
  '&:hover': {
    color: '#ffffff',
    cursor: 'pointer'
  },
  '@media (max-width: 700px)': {
    backgroundColor: '#ff0000'
  },
}));

class App extends Component {
  render() {
    return (
      <ThemeProvider theme={theme}>
        <MyDiv>
          React + Emotion rocks!
        </MyDiv>
      </ThemeProvider>
    );
  }
}

Here you can see the basic example implemented with Emotion using an object:

And here you can see it implemented using a tagged template:

Wrapping up

A good page to learn how popular are these libraries is npm trends. Here’s a snapshot of the statistics at the time of this writing (here you can find the most recent ones):

And here’s a summary of the features of these libraries:

Evaluated Aspect Radium Aphrodite Emotion
License MIT MIT MIT
Latest version (at the time of this writing) 0.24 2.2.2 9.2.3
Size 17.9k (gzipped) 19.1k/6.15 (gzipped) 14.4k (core)/5.58k (gzipped)
Documentation 9.0/10.0 8.5/10.0 9.0/10.0
Syntax type object literal object literal object literal/template literal
CSS type styles classes (for some cases) classes classes
Autoprefixing Yes Yes Yes
Server side rendering Yes Yes Yes
Pseudo classes (:hover; nth-child; etc) Only supports :hover; :focus, and :active Yes Yes
Pseudo elements (::after; ::before, etc.) No Yes Yes
Media queries Yes Yes Yes
Nesting Yes Limited Yes
Keyframes Yes Yes Yes
@font-face No Yes Yes
Native support for themes No No Yes
API Yes No No

If you ask me, all things being equal, I’d say my favorite library is Aphrodite.

I feel this library has the best balance. Radium lacks some features while Emotion, although it’s flexible and it has great documentation, it has so many features that it’s a bit complex to use sometimes. If you ask me, all things being equal, I’d say my favorite library is Aphrodite.

But when deciding what library to use in your project, the most important thing is to take into account the aspects that are relevant for your project. Don’t choose the library with the most features or yeses in the table.

Finally, also check out this repository where Michele Bertoli compares a lot more libraries for React and implement an example with each of them.

Full visibility into production React apps

Debugging React applications can be difficult, especially when users experience issues that are difficult to reproduce. If you’re interested in monitoring and tracking Redux state, automatically surfacing JavaScript errors, tracking slow network requests and component load time, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens on your React app. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors performance of your app with metrics like client CPU load, client memory usage, and more.

The LogRocket Redux middleware package adds an extra layer of visibility into your user sessions. LogRocket logs all actions and state from your Redux stores.

Modernize how you debug your React apps - .

Esteban Herrera Family man, #Java and #Javascript developer. #Swift, and #VR/#AR hobbyist. Like #books, #movies and still trying many things. eherrera.net

Leave a Reply