This was a novel task for me. As a frontend speed & scalability guy, I often get involved with the websites when their frontend choices & setup are long in place. And often, my first job is to find the solutions living within the constraints of those choices. But, here, I had to provide inputs to help make that choice. 😊
How I feel when starting a speed optimization assigment!
Before I talk about the findings from the exercise, here’s a quick brief on the friction in the web development world with respect to CSS-in-JS, good developer experience (DX) and web performance.
CSS-in-JS libraries like styled-components and emotion solve this by enabling us to write component-centric styling. Having the styling code closely tied to the component JavaScript enables dynamic styling and easier maintenance. This, in-turn, results in an improved DX.
But, making the UI styling JavaScript driven also means we make it JavaScript dependent at runtime. This has performance implications:
As a result of this, all the DX goodness of CSS-in-JS and top-notch web performance appear like an either-or.
So, what is the speed impact of using CSS-in-JS? To measure this, I created two versions of the below page (with NextJS) - one version styled via CSS modules and another with styled-components.
And, below are the measured performance numbers:
Measured via WebPageTest : Fast 3G | Moto G4 | US-East-1
Measured via Chrome devtools (Network tab)
Measured via Chrome devtools (Performance tab) during page re-rendering
I then decided to evaluate zero runtime CSS-in-JS. More specifically, I evaluated linaria and studied astroturf.
These are CSS-in-JS libraries so we get to write our styling within the component files. They extract the CSS into separate CSS files during the build process. They do not require runtime JavaScript execution for styling. These libraries achieve dynamic props based styling via CSS variables at runtime.
So, I created a linaria styled version of the same page used for benchmark. This gave an opportunity to compare how similar it’s styling is to styled-components:
const Button = styled.button`
border-radius: 3px;
margin: 0.5em 1em;
padding: 0.25em 1em;
${props => props.primary
&& props.color && css`
background:
${props.color};
border: 2px solid
${props.color};
color: white;
`}
${props => !props.primary
&& props.color && css`
background: transparent;
border: 2px solid
${props.color};
color: ${props.color};
`}
`;
const Button = styled.button`
border-radius: 3px;
margin: 0.5em 1em;
padding: 0.25em 1em;
background: ${props =>
(props.primary ?
props.color :
'transparent')};
color: ${props =>
(props.primary ?
'white' :
props.color)};
border-width: 2px;
border-style: solid;
border-color: ${props =>
(props.color)};
`;
const Button = styled.button`
border-radius: 3px;
margin: 0.5em 1em;
padding: 0.25em 1em;
${props => props.primary
&& props.color && css`
background:
${props.color};
border: 2px solid
${props.color};
color: white;
`}
${props => !props.primary
&& props.color && css`
background: transparent;
border: 2px solid
${props.color};
color: ${props.color};
`}
`;
const Button = styled.button`
border-radius: 3px;
margin: 0.5em 1em;
padding: 0.25em 1em;
background: ${props =>
(props.primary ?
props.color :
'transparent')};
color: ${props =>
(props.primary ?
'white' :
props.color)};
border-width: 2px;
border-style: solid;
border-color: ${props =>
(props.color)};
`;
Unrelated to performance, but something of paramount importance - linaria’s similarity can make it’s adoption easier and future migrations simpler. However, I’m not sure if other zero runtime CSS-in-JS libraries are as similar to styled-components as linaria.
So, how does the linaria styled version of the same page perform compare to the other versions? Below are the performance numbers:
Measured via WebPageTest : Fast 3G | Moto G4 | US-East-1
Measured via Chrome devtools (Network tab)
Measured via Chrome devtools (Performance tab) during Page Re-rendering
Based on the undertaken benchmarks and having written (very limited) linaria styling, my findings from this exercise were as following:
Based on the above findings, we decided to undertake a more detailed evaluation of linaria. To do so, we plan to style a few components and pages for the new website with linaria. During this process, we plan to check on the following aspects:
Note : Source code for the benchmark used to derive the results shared above is located here.