When preloading stylesheets does (not) make sense

Exploring the considerations of preloading stylesheets for web performance optimization.

Arjen Karel Core Web Vitals Consultant
Arjen Karel
linkedin

When Preloading Stylesheets Does (Not) Make Sense

I regularly encounter preloaded stylesheets and a lot of misinformation surrounding them. Preloading a resource (usually) changes it's priority and the time it is scheduled for download. However, like many optimization strategy's that I come across daily, preloading stylesheets does not make much sense most of the times. In this article, I'll  explore when preloading stylesheets makes sense and when it might not be the best choice.

The idea of preloading stylesheets:

Before diving into the considerations, let's briefly revisit the concept of preloading stylesheets. Stylesheets are render blocking. This means that while stylesheets are being downloaded the page will not be rendered by the browser and all the visitor will see is a blank screen. 

To minimize the time it takes to download a stylesheets developers will sometimes resort to preloading stylesheets. Preloading involves fetching critical resources in advance, minimizing the latency associated with loading them when they are actually needed. This is typically achieved using the <link> tag with the rel="preload" attribute.

Case 1: preloading the stylesheet just before declaring it.

Sometimes developers, in all their enthusiasm, try to minimize CSS impact by reloading it in the HTML just before the actual CSS declaration. This will look something like:

<link rel="preload" as="style" href="style.css">
<link rel="stylesheet" href="style.css">

Now this does not make sense at all and at best you will not slow down the page! A browser will read the html and start loading all the critical resources on the page, mostly in the order in which they find it. This means that if you removed the preload line the browser would have found the stylesheet anyway and would have started downloading the stylesheet no matter what. You just added an extra line, that is all! But it get's worse. Preloaded stylesheets get a lower priority then normal stylesheets. So in the worst of circumstances you would actually slow down your page!

3 preloaded stylesheets

stylesheet preloaded

3 normal stylesheets

stylesheet not preloaded

Case 2: preloading the stylesheet with a link header.

This idea is interesting. We can use the server link server header to preload a stylesheet. That would look something like this:

Link: <style.css>; rel=preload; as=style

The idea here is to get the browser to enqueue the stylesheet before it starts parsing the HTML. This is a pretty good idea and I like how the mind of a developer who thought of this works! But unfortunately in real life you will get very little benefit from this. We are talking about a few microseconds. Those are pretty disappointing results but dont worry. We can use this step to make some real improvements!

Case 3: 103 early hints for stylesheets

This is the only idea that will actually get you some Core Web Vitals results! Sending early hints for stylesheets will improve metrics like the first contentful paint and the largest contentful paint.

103 early hint headers are send before the actual html response. That means that while you are downloading the HTML, the browser may also start downloading stylesheets in parallel.  The best case scenario is that by the time that the HTML has finished downloading the stylesheets might have downloaded as well. This means that there is zero render blocking time from those stylesheets. And this can and does happen on slower networks! Omn fater networks the effect is less obvious but using 103 early resource hints is still faster in almost all cases!

A 103 early hint reponse looks much the same as a preload link header. To use 103 early hints you will need to configure your webserver or your CDN.  

HTTP/1.1 103 Early Hints
Link: </style.css>; rel=preload; as=style

Some CDN's like CloudFlare will let you trigger n 103 early hint by simply setting a link preload header (as described in idea 2)

Case 4: preload stylesheets to achieve asynchronous CSS

A well known trick to make CSS non-render-blocking is to preload the stylesheet and once it is loaded add it to the page. The idea is simple and looks like this:

<link 
   rel="preload" 
   href="style.css" 
   as="style"
   onload="this.onload=null;this.rel='stylesheet'"
>

    It is based on the normal preload code <link rel="preload" as="style" href="style.css"> and adds an onload event listener onload="this.onload=null;this.rel='stylesheet'" that changes the link to to it's final form <link rel="stylesheet" href="style.css">

    This is another idea that just makes sense. If a stylesheet is not render blocking the browser can continue to parse and render the page without the need to stop and wait for the stylesheet. However be  careful!

    • Do not async CSS in the visible viewport. This will likely cause a Cumulative Layout Shift and that will lead to a poor User Experience
    • Consider the trade-off. Async CSs will likely cause a re-render of different parts of the page. This will impact metrics like the Interaction to Next Paint. 

    Case 5: pre-cache stylesheets

    Om rare occasions I see nifty solutions that try to pre-warm cache files for subsequent pageviews. And while I applaud the enthusiashtm with which those solutions are made. Please do NOT do this!

    The idea is simple: on the homepage you could, if you wanted to, already pre-cache the styles for another page. Let's say the product page. At some point after page load you would preload stylesheets to add them to the browser cache.

    function preloadStylesheet(url) {
        var link = document.createElement('link');
        link.rel = 'preload';
        link.href = url;
        link.as = 'style';
        document.head.appendChild(link);
    }
    
    window.addEventListener('load', function () {
        preloadStylesheet('cart.css');
        preloadStylesheet('shop.css');
    });

    At first glance this does not look half bad. On any product page cart.css and shop.css are now available and will not block render anymore. There are a few reasons not to do this!

    1.  There are better ways, for example speculative prerendering or by using a service worker.
    2. You will probably waste resources and the trade off will not be worth it! Let's face it: if preloading resources was easy browsers would have been better at it. 
    3. Solutions like this are hard to maintain and will probably cause issues at some point in the future
    4. You will need to preload all stylesheets, not just a few. Because all stylesheets are render blocking and downloaded in parallel just 1 stylesheet can have the same effect as multiple stylesheets.


    I help teams pass the Core Web Vitals:

    lighthouse 100 score

    A slow website is likely to miss out on conversions and revenue. Nearly half of internet searchers don't wait three seconds for a page to load before going to another site. Ask yourself: "Is my site fast enough to convert visitors into customers?"

    When preloading stylesheets does (not) make senseCore Web Vitals When preloading stylesheets does (not) make sense