Hello

I'm Alejandro

A Web Engineer from Málaga, Spain based in London, UK

React performance with CSS

There are a lot of different ways to do CSS with React, and the library itself is not opinionated on how you do so, instead, it’s you, the developer, who decides what to use.

Reality is, that if you look for “Stying a React App” in google, it will come up with hundreds of articles with so many choices (classNames, postcss, css-in-js...etc).

In this post, I’m going to go a bit beyond styling and explore what it means to make a choice in what styles refers to.

I’m going to run through some examples of how these CSS approaches using React perform in the real world. This blog post is inspired on a talk from Kasia Jastrzebska at React Alicate 2019 (Shout out to her for the amazing talk! 🎉), in which she shows different ways to use CSS in React and why they may be more performant than others.

I'm not going to opinionate what you currently use, but instead suggest options for your real world application.

As I said previously, there are a lot of ways to do CSS with React, but we are going see five of them:

  • Inline styles
  • Vanilla CSS
  • CSS Modules
  • Styled Components
  • Emotion

For this example, I’m going to work with a List component, in which Items are constructed with a different CSS approach each time. To see the difference in real world performance, for each example I am going to render 5000 items.

This is how the App will look like:

import React from 'react'
import './App.css'
import Item from './item/item0'
// import Item from './item/item1'
// import Item from './item/item2'
// import Item from './item/item3'
// import Item from './item/item4'

function App() {
  return (
    <ul className="list">
      {Array(5000)
        .fill()
        .map((_, i) => {
          return <Item key={i}>Item {i}</Item>
        })}
    </ul>
  )
}

export default App

Note: Array(number).fill() will create an array of x items on the fly.

Inline styles

Maybe, one of the simplest ways to style a React application, just because there is no need of configuration. React provides a prop called style which allows to pass an object with camelCased css properties.

This is how the Item component will look like using this approach.

import React from 'react'

function Item({ children }) {
  return (
    <li
      style={{
        border: '1px solid',
        padding: '10px',
        margin: '5px',
      }}
    >
      {children}
    </li>
  )
}

export default Item

Simple right? Let's measure. For that, we are going to open chrome dev-tools and go to the "Performance" tab.

This is the result.

Image of inline styles performance

This is, from my point of view, a bit high on scripting to be just inlined right? But we need to compare before we can judge.

className

With a regular .css file that we need to import on our Item component.

import React from 'react'
import './item.css'

function Item({ children }) {
  return <li className="item">{children}</li>
}

export default Item

And the result is...

Image of classname performance

Better right? Let's continue.

CSS Modules

This approach need a bit more configuration, but it will allow to map our css classes with className in object notation.

If you use create-react-app, postcss configuration comes out of the box, just creating .module.css files.

import React from 'react'
import styles from './item.module.css'

function Item({ children }) {
  return <li className={styles.item}>{children}</li>
}

export default Item

Result?

Image of CSS Modules performance

Looks like the same, sort of.

styled-components

One of the most popular libraries to style React applications in the React community. And it's that polular because it allows you to separate corncers and logic coming from styles and data without having to deal with className or any of their variations in your render function, instead, all the presentation logic will be on the styled element itself.

We all love that 😍.

import React from 'react'
import styled from 'styled-components'

const Li = styled.li`
  border: 1px solid;
  padding: 10px;
  margin: 5px;
`

function Item({ children }) {
  return <Li>{children}</Li>
}

export default Item

But...

Image of styled-components performance

Wow! It has doubled the amount of scripting. Did you spect that?

Emotion

import React from 'react'
import styled from '@emotion/styled'

const Li = styled.li`
  border: 1px solid;
  padding: 10px;
  margin: 5px;
`

function Item({ children }) {
  return <Li>{children}</Li>
}

export default Item

Very similar to styled-components, lets see how they compare.

Image of emotion performance

Now, you should judge yorself.

Conclusion

I think, when you have seen all the performance results together, it all make sense... except one.

Why is Inline styles worst than className? Well, in fact, if you refer to the official react docs, it mentions that inine styles are not that performance. It's all about these lines of code.

for (propKey in lastProps) {
  ...
  if (propKey === STYLE) {
    const lastStyle = lastProps[propKey]
    for (styleName in lastStyle) {
      if (lastStyle.hasOwnProperty(styleName)) {
        if (!styleUpdates) {
          styleUpdates = {}
        }
        styleUpdates[styleName] = ''
      }
    }
  }
}

This is some of the code React executes everytime a component updates. There is a special case for the style prop in which if there are changes on it, it will loop all over the CSS properties and empty them fisrt, and then apply the new style properties. That seems pretty slow right? And even more when we are talking about modifying DOM elements.

className performance is pretty awesome, as there is no scripting involved. All the CSS goes to a separate file.

The same for CSS Modules, which has a little bit more scripting time because we are dealing with objects instead, nothing to be concern at.

styled-components and emotion seem pretty heavy. This is because they are shipping a runtime to the browser. What does it mean?

When React creates the elements on your application, those are going to generate a className and assign it to itself. Imagine all that happening to all the elements within your application at once on your browser. All that code is bundled and loaded on the client.

You might find some wins if you do Server Side Rendering, because all that may have already happened on the server. But notice, it will still ship a small runtime with it.

My point of view on these last two, is that they focus on developer experience, and this is a big win, the cleanest and fastest we develop, the more features we can deliver and the easies we can maintain it. It's good to find the balance between performance and maintainability.

So now, go and choose your React styling approach, but keep in mind, some of them might be more performant than others.

Share this post