Styled components vs Emotion js: Performance perspective

Originally published atdev.to

Published on Wed Aug 19 2020

Ever wondered how your styles make it to the browser when using CSS-in-JS libraries? When using css in js libraries such as styled-components or emotion, the library removes the mapping between components and styles. This means that when you're defining your styles, you're actually creating a normal React component, that has your styles attached to it. This means you are shipping your styles in a .js file rather than a .css file. This has performance cost associated.

In this article I’m going to take a look at build sizes for emotion vs styled-components. Apart from page rendering, build sizes directly affect the load time. Shipping large builds will naturally have bad performance. If the website relies on traffic from organic search and PPC campaigns, page speed is turning on to be a more important factor.

In my setup, I spin up a Next.js boilerplate. You can do this by running

npx create-next-app

OR

yarn create next-app

Boilerplate

Now, without doing any further changes, Let’s run a build on the basic boilerplate version

We have a 61.1 kB of FIRST LOAD JS.

Plot

In this assessment, I only add a styled header component which I create in the <MyComponent /> component.

mycomponent/index.js

import { Header } from './styles';

const MyComponent = () => <Header>Styling this component with emotion-js</Header>;export default MyComponent;

mycomponent/styles.js

export const Header = styled.h1`
color: blue;
`;

Versions used ->

  • "react": "16.13.1"
  • "@emotion/core": "10.0.35"
  • "next": "9.5.2"
  • "styled-components": "5.1.1"

Let’s go:

First up - styled-components

We have a 20% increase in our build size right away.

One can argue that a 13kB increase doesn’t make any difference, however, in page speed performance - milliseconds matter & so do the amount of bytes you ship across the network.

Let’s take both the builds on a test drive and deploy them on Vercel.

After deploying, I ran a page speed comparison on https://developers.google.com/speed/pagespeed/insights/

Results:

19% difference in Time to interactive
28% difference in First contentful paint
And 2 points skimmed off from the page speed score

Apart from the build sizes, the other performance hit is “rendering“ & “react re-renders“. The CSS-in-JS libraries depend on a runtime that helps them dynamically update the styles of a component. These CSS-in-JS libraries don’t create CSS classes at build-time, but instead dynamically generate and update <style> tags in the document whenever a component mounts and/or has its props changed making it favourable for theming & complex use of css.

If there’s such a difference in the tiniest example possible, a even more complex app can have heavier build sizes. Also, since we’re shipping our styles in a javascript file, it’s evident that increasing the number of styled components will increase the build size and hence reduce the page speed.

Next up - Emotion.js

Emotion.js performs slightly better than styled-components.

Given that we know the how these libraries perform with build sizes, It will be interesting to see which one is faster when rendering them on to the browser. Faster rendering on browsers will give us a lower “time to interactive“. This should be an interesting case study that demands an article of it’s own.

Here’s the Page speed comparison for both the libraries -

We can see emotion stands somewhere midway between a plain boilerplate and styled-components.

After working with both the libraries extensively, I did not find a big difference in the JS APIs and by developer experience (DX) was the same. If you’re using an older version of styled-components, your build sizes will tend to be much larger. Lately, the styled-components team bridged the gap by reducing their build sizes.

If you’re project doesn’t handle theming or complex css, linaria can be a suitable option as it compiles js into css at build time.

What is your go to CSS-in-JS library?