menu

Modern Asynchronous CSS Loading

The simplest way to load a CSS file in an HTML document is to use a link element with rel="stylesheet":

<link rel="stylesheet" href="mycssfile.css">

Referencing CSS this way works great, but it comes with a downside: it’s synchronous. In other words, with a typical stylesheet link like this, the browser stops rendering subsequent portions of the page while it requests, downloads, and parses the file. Sometimes that blocking can be desirable because we don’t want the browser to render the page before it has the CSS it needs. But not all of our CSS files are critical enough to delay access to the content, which is why we highly recommend prioritizing and streamlining assets for fast, resilient delivery.

To load less-critical CSS files without blocking page rendering, we need to load them asynchronously.

Another way is to set a stylesheet link's media attribute to a media type (or query) that does not match the user’s current browsing environment, say perhaps media="print", or even something unrecognizable like media="nope!". Browsers will consider stylesheets for inapplicable media to be low-priority, and download them without blocking page rendering. That’s good to know, but in order to display the stylesheet’s rules once it loads, we need to use a JavaScript onload event handler to toggle the media value to something that matches the user’s browsing environment, like screen or all:

As a side note: we use a combination of the tricks above internally in our loadCSS.js library to handle asynchronous CSS loading, along with additional workarounds for old/still-active browsers that don’t support onload events on link elements.

Similar to the media toggle approach, we could also load CSS asynchronously by marking a link as an alternate stylesheet (designed to offer the user alternate presentations of a site), and then using JavaScript to toggle the rel attribute back to stylesheet when the file loads:

Thankfully, there’s now a web standard that is designed specifically for loading resources like CSS asynchronously: rel="preload". Finally, we can load asynchronous CSS without JavaScript workarounds! Just kidding; perhaps surprisingly, even this method requires an onload event handler to make it work as we need. Still, it’s our best option.

Here’s an example of how we can use rel="preload" to load and apply a CSS file asynchronously (in a browser that supports it):

Much like other attribute-toggling approaches above, rel="preload" will cause supporting browsers to download–but not apply–the referenced file, so again we need an onload handler to set the rel attribute to stylesheet once it finishes loading. This may not look like a big improvement over other approaches, but one advantage that rel="preload" brings is that supporting browsers will start downloading the file earlier than they would with say, a stylesheet with a non-matching media value.

Browser support for rel="preload" is, well… I mean, at least Chrome supports it. Other major browsers have committed support too, though. In fact, Firefox 56 already did support rel="preload", but its implementation was buggy enough (preload only worked for files that were explicitly deemed cacheable) that Firefox 57 shipped with the feature disabled (support will return again in Firefox 59).

Fortunately, we can test and polyfill support for rel="preload" to make sure it works everywhere. Our loadCSS project offers a script called cssrelpreload.js, which makes rel="preload" work for CSS files in browsers that don’t support it natively (and it knows to do nothing if the browser knows how to handle preload on its own).

NOTE: if you’ve used cssrelpreload.js before, the latest version (2.0.1) has some nice improvements, and it no longer depends on the loadCSS.js script, so you can stop including that file and save some precious kilobytes.

As with any of our open source projects, you can find loadCSS on Github and on NPM. If you run into any issues, have questions, or want to contribute, please let us know in the issue tracker.