DEV Community

Cover image for Styled components vs Emotion js: A performance perspective
Meet Dave
Meet Dave

Posted on

Styled components vs Emotion js: A performance perspective

This article demonstrates the bare minimum difference on build size when using a css-in-js library & it's performance implications.

Ever wondered how your styles make it to the browser when using CSS-in-JS library like styled-components or emotion?

When we're defining our styles, we're actually creating a normal React component that has the styles attached to it. This means we are shipping the styles in a .js file rather than a .css file.

We're going to take a look at build sizes for the two most used CSS-in-JS libraries: emotion.js vs styled-components.

Apart from page rendering performance, build sizes directly affect the load time. Shipping large builds will naturally have lower load times & an increase in site's time to interactive. If the website relies on traffic from organic search and PPC campaigns, page speed is an important factor.

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

$ npx create-next-app
Enter fullscreen mode Exit fullscreen mode

OR

$ yarn create next-app
Enter fullscreen mode Exit fullscreen mode

Boilerplate build

Now, without doing any further changes, Let’s create a boilerplate production build.

We have 61.1 kB of FIRST LOAD JS.

Alt Text

Plot

In this assessment, We only add a styled header component which we create in the component.

mycomponent/styles.js

export const Header = styled.h1`
  color: blue;
`;
Enter fullscreen mode Exit fullscreen mode

mycomponent/index.js

import { Header } from './styles';

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

export default MyComponent;
Enter fullscreen mode Exit fullscreen mode

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

Alt Text

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

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

Let’s take both 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:

Alt Text
Alt Text

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. The 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 it's props changed, making it favourable for theming & complex use of css with React.

If there’s such a difference in the tiniest example possible, an 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

Alt Text

Emotion.js performs slightly better than styled-components.

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

Alt Text
Alt Text

In terms of build size, we can see emotion js 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. There wasn't much difference in developer experience (DX) as well. If you’re using an older version of styled-components, your build sizes will tend to be much larger. Lately, the styled-components team have bridged the gap by reducing their build sizes.

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

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

Top comments (5)

Collapse
 
timothy profile image
Timothy

Great article! Is the data you get under "First Load JS" specific to Next? I'm using Gatsby and on executing a build I only get times, not sizes (see attachment). How do I get something like yours? Not sure if I am missing something obvious, but please enlighten me :)

Collapse
 
meetdave3 profile image
Meet Dave

Hi Timothy! Thanks for appreciating. Hoping the write up has helped you in some way. :)

For Gatsby, yes there is a webpack plugin which you need to configure in your gatsby-config.js file. The plugin goes by the name: gatsby-plugin-webpack-size

Collapse
 
timothy profile image
Timothy

Great I'll try it!👌

Collapse
 
juicycleff profile image
Rex Raphael

Great content.

Collapse
 
kingkong12 profile image
Mithil

get info , was worth the time