How the introduction of the CSS in JS can improve the first render time of a Progressive Web Application
15 Jun 2018
The key benefits are:
CSS isolation: CSS class names are dynamically generated; they are typically smaller and more isolated than regular CSS.
Download size: Small CSS names and the embedding of CSS in the application bundle leads to fewer network requests.
Code sharing: All code is in the same location and therefore you can easily share constants like colours and sizes between CSS and application code.
The usage of CSS in JS is obviously not the solution to all performances problems and there are many developers who heavily criticize the concept behind it, for example, posts such as this . Several solutions also exist that enable important performances to benefit by just using plain CSS code, for example, CSS modules . As always, as developers, we just have to choose the tool and technique that best fits the product requirements and team preferences.
In this article, we demonstrate how to embrace CSS in JS in a React application.
Choosing a CSS in JS tool
There are several open source CSS in JS tools available. Looking at a non-exhaustive sample list like this there are more than twenty options.
All the tools provide more or less the same functionality and most of the time are framework-agnostic, so the choice is really up to you.
In our case, we want to choose tools that are easy to introduce in the stack without too much complexity. So all tools that use the Higher Order Component (HOC) approach are excluded.
HOC uses the idea that a developer passes a component to a function which adds new functionality. The result is then used in place of the original component.
Amongst all existing CSS in JS tools, we chose TypeStyle for the following reasons:
It has complete TypeScript typings. For editors that support it, like Visual Studio Code, it enables complete static analysis and unused code detection at development time.
It is framework-agnostic and can be embedded easily into most frameworks without fundamentally changing the way you write your application.
It has utilities and related packages for media queries, colours and animation.
Since rules are appended to a <style> in the page, generation of the bare minimum CSS in server-side rendering (the so-called critical path) is automatic.
The core idea of writing CSS in JS is that you call the style function passing your CSS rules. The return value of the function is a class name you can use when you need.
The generated class name is something like f14svl5e. This name (actually is a hash of passed properties) is generated from the rules you pass in. There is no collision. Also, calling this function automatically appends this rule to the list of rules to register.
To get started, let's define our rules. We create a little spinner using TypeStyle in React:
In this example, we demonstrate most of the features you typically use.
Firstly we define a colour using,csx which is a utility package from the same developers of TypeStyle.
Once again, the keyframes function returns a string - we don't care what it is, we just have to pass it over to the style.
Then we define the two classes needed for this example: one for the SVG itself and one for the inner circle that spins. In the latter we reuse the animation name; this is unique across all generated styles so no collision happens.
We have also defined a media query using the media function. This takes the media query as the first argument and the rules object as the second argument. To test it just make the window smaller and the spinning velocity increases.
Lastly, we pass the returned values as class names as the values of the className property and were done!
Once the application is loaded and the style functions are evaluated, TypeStyle creates a <style> tag in the document head and appends all the rules there.
This is the code that is generated:
And this is the added <style> element:
Generic Rules and Advanced Media Queries
TypeStyle also lets you define rules with custom selectors. These rules are applied immediately to the document and no class names are generated. Let's say we want to show our spinner in the centre of the page. We take advantage of the CSS Flexbox module and this task is trivial.
Going back to media queries, there is something to consider when working with them. While CSS is applied top-bottom, TypeStyle rules are not. Collision-free classes names usually ensure this is not a problem, but media queries targeting the same class may show some unexpected behaviour.
Take this stylesheet example:
When applying this to a document the media queries are executed top-bottom, therefore when the window size is below 400 pixels the div text colour is yellow.
When using TypeStyle, you typically translate the stylesheet above to the following definition:
TypeStyle doesn't guarantee the order of generation of the classes (especially since this is dependent on the JS engine), so we need to adapt the media queries not to generate ambiguity.
The code above can be fixed this way:
Server-Side Rendering and Critical CSS
TypeStyle makes Server-Side Rendering (SSR) very easy. The core idea is that once you get your server-generated HTML ready in some variable, you make a call to the getStyle function to flush all the used styles so far.
This also has the benefit of ensuring that only used CSS rules are sent in the response - this is the so-called critical CSS .
Here is a simple example of how to generate a full HTML page using React and TypeStyle on the server:
Drawbacks and Improvements on the age Performances
Embedding TypeStyle in the code, of course, has its drawbacks. In particular, the addition of the 2 new libraries increased the PWA application code size by roughly 15KB.
The good news is that while this additional data is loading, the application and the user interaction is not blocked at all. The application has already been rendered on the server and all the CSS required has already landed on the device with the HTML page; so the browser has everything it needs to render the page.
Also, regarding the increase - when in fast network environments such additional data is hardly perceivable, on slow or unreliable networks it is balanced by huge benefits on the first-render time.
This was the original performance of page load on the original branch using just plain CSS.
By using TypeStyle, we've been able to almost reduce the first render time on a slow 3G network by 50%.
To give an overview on how TypeStyle helps in limited situations we have run the same comparison on a different mobile network condition using the profile settings used by webpagetest :
Download Speed (Kbps)
Upload Speed (Kbps)
As you can see from the graphs below the benefits are more perceivable in the slower networks where each network request is really expensive. Even in most performant network; embedding using the CSS in JS technique reduces the first render time by 100ms.
In all cases, CSS in JS increases the first interactive at most by 100ms. As outlined before, the decrease of first render time makes it acceptable if we look at the increase in users conversion rates.
The introduction of PWAs has radically changed the way we develop our applications. Mobile devices are now capable of storing information offline in ways that were not even imaginable a few years ago.
PWAs introduce various new ways of serving content in unforgiving network environments. We must not forget that there needs to be a network to load the cache in the first place. Every application is different and the introduction of service workers allow developers to provide custom caching strategies which can better fit the specific use case.
In this post we have outlined how using CSS in JS can help in reducing the application size and the amount of network requests required to first load it. While this technique is not a silver bullet for all modern Web application performance problems, it leads to a significant reduction in the first render time.
Insight, imagination and expertly engineered solutions to accelerate and sustain progress.