There are many ways to improve a website performance. One of the ways is to preload specific files that you know will be required later and are essential for the quick rendering of the page.
The preload
value of the rel
attribute in the <link>
element allows you to declare in the HTML head that certain assets need to be loaded as early as possible during the page’s lifecycle.
Preload enables you to initiate a request for a specific file and store it in memory before the actual need for the file, ensuring that it is available as early as possible and reducing the likelihood that this file will delay the page render.
Proper asset loading on a page is an important aspect, and you need to do it correctly to achieve good results in the First Meaningful Paint metric.
In the real world, websites load several files, including CSS files, fonts, JavaScript, and images. These assets block loading by default, a situation that affects the loading time of the site or application.
So, in this post, we will focus on <link rel="preload">
– one relatively new feature called Resource Hints.
There is also an article about using preconnect for Google Fonts, which is suitable for specific situations. However, in the case of fonts, the use of preload, which we are discussing in this post, is the more appropriate solution.
About the “rel=”preload” Attribute
The usage of preload is as follows:
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="main.js" as="script">
- The path to the asset is specified in the
href
attribute. - The type of asset is specified using the
as
attribute.
What types of content can be preloaded using preload?
Preload can be used for various types or formats of files, including but not limited to CSS files, JavaScript, fonts, images, and more. Here is a partial list of supported file types:
- Audio – an audio file used with the <audio> tag.
- Document – an HTML document intended to be embedded using <frame> or <iframe>.
- Embed – an asset embedded using the <embed> element.
- Font – a font file.
- Image – an image file.
- Script – a JavaScript file.
- Stylesheet (CSS) – CSS Stylesheets files.
There are additional types of content not mentioned in this list; take a look at MDN web docs.
MIME Type Declaration
The <link>
element can have the type
attribute where you can specify the MIME type of the linked asset.
MIME type declaration is particularly useful when preloading assets because it prompts the browser to check if it supports the file type specified in type
and only download it if it does.
<link rel="preload" href="movie.mp4" as="video" type="video/mp4">
We won’t delve further into MIME types; feel free to check the same MDN docs mentioned earlier for more information.
Usage of Media Queries
As you know, you can use media queries with the <link>
element, allowing you to preload based on the user’s viewport.
Let’s say there is a crucial image at the top part of the page that you want to preload. In many cases, you’ll want to display a different image to mobile users than to desktop users, either for design reasons or to load a smaller-weight image for mobile users.
In both cases, you’ll want to preload this image to ensure that you display the main content as quickly as possible.
<link rel="preload" href="mobile-img.jpg" as="image" media="(max-width: 600px)">
<link rel="preload" href="desktop-img.jpg" as="image" media="(min-width: 601px)">
A Few Words on Cross-origin
If CORS is configured correctly on your server, you can preload assets from another origin (different domain) as long as you add the crossorigin
attribute to the <link>
element.
For various reasons, fonts are an exception, and you need to provide the crossorigin
attribute even if those fonts are on your server.
<link crossorigin rel="preload" href="fonts/assistant.woff2" as="font" type="font/woff2">
When Should We Use Preload?
Preload instructs the browser to download and store a specific asset in memory as quickly as possible. It is useful when you know that you will need a certain asset immediately after the page loads, and you want to start loading it earlier.
It’s essential to understand that the browser doesn’t perform any actions with that asset after downloading it. It doesn’t execute scripts, and it doesn’t apply CSS. The only thing <link rel="preload">
does is store the asset in memory, so when someone requests it, it’s immediately available. Let’s look at two simple examples:
Example A
For instance, suppose you are loading some web font using font-face@, and the CSS for this font is in an external file. Then, for the sake of this example, let’s assume this is our HTML file:
<link rel="stylesheet" href="index.css" />
And let’s assume this is the CSS file:
@font-face {
src: url('assistant.woff2') format('woff2');
}
By default, assistant.woff2
will start downloading only when index.css
is loaded and applied. Instead of waiting for this point, we can use preload to initiate the request earlier.
This way, it will be available faster for rendering, and users will experience less FOUT (Flash of Unstyled Text). We discussed this in a post talking about the font-display property.
It’s worth mentioning that it’s advisable, and even recommended, to use preload along with the font-display property to optimize font loading.
Example B
Another example is a scenario where you split your style files for a quicker display of the critical path of the page. One file is for the critical part, and the second file is for the rest of the page:
<style>
/* Inlined critical styles */
</style>
<link rel="preload" href="/non-critical.css" as="style" />
<script>
/* Non-critical styles */
loadCSS('/non-critical.css');
</script>
In this case, the non-critical style file will only start downloading when the JavaScript runs, which could happen quite late after the initial render. Instead of waiting, you can initiate the preload to save precious time.
So, What Are Render-Blocking Resources?
Before we continue, let’s explain what is meant by Render-Blocking Resources.
When a request for a specific asset blocks rendering, it essentially means that the window.onload
event won’t be triggered until the request is completed, and the asset is fully downloaded.
Modern Single Page Applications (SPAs), for example, rely heavily on this event
to start operating. This means that parts of the user interface won’t render until those render-blocking requests finish loading.
Take a look at the following code:
<html>
<head>
<link
rel="stylesheet"
href='https://fonts.googleapis.com/css?family=Roboto:400,600|Material+Icons'>
<style>
html {
font-family: Roboto;
}
</style>
</head>
<body>
Hello
<script>
window.onload = function () {
console.log('Loaded');
}
</script>
</body>
</html>
If I load an HTML file in the browser and look at the Network tab in Chrome Dev Tools, I will see that the text Loaded is logged in the console exactly after the CSS file is loaded, as illustrated in the following image:
To distinguish the result, I throttled the connection speed to Slow 3G.
Let’s change the link
tag to initiate the request earlier:
<link
rel="preload"
as="style"
href='https://fonts.googleapis.com/css?family=Roboto:100,900|Material+Icons'>
Now, if I clear the cache and refresh the page, I will notice (with much attention) that the text Loaded is displayed exactly before the request for CSS begins, as illustrated in the following image:
Wait, did you notice the orange comment that appeared in the console? The comment warns us that we preloaded the asset but did not use it in practice.
As we explained, preload only caches the asset but does not apply it. That means the decision of when to use this asset is up to you in this specific case.
Um, so how do we apply the CSS?
In this example, we want to apply the CSS file as quickly as possible once it’s loaded to display the text immediately with the same web font. To achieve this, we can modify the code as follows:
<link
rel="preload"
as="style"
onload="this.rel = 'stylesheet'"
href='https://fonts.googleapis.com/css?family=Roboto:100,900|Material+Icons'>
By setting the rel
attribute to stylesheet
, we instruct the browser to use this asset. Since it’s already cached due to the use of preload, it doesn’t re-download the asset but applies it immediately.
As this solution relies on JavaScript, it’s advisable to add the <noscript>
tag as a fallback for situations where JavaScript is disabled in the browser:
<link
rel="preload"
as="style"
onload="this.rel = 'stylesheet'"
href='https://fonts.googleapis.com/css?family=Roboto:100,900|Material+Icons'>
<noscript>
<link
rel="stylesheet"
href='https://fonts.googleapis.com/css?family=Roboto:100,900|Material+Icons'>
</noscript>
This is the modern and correct approach for loading CSS and applying it immediately using preload.
What about preloading JavaScript?
Loading JavaScript is done differently. As mentioned, assets loaded through preload are cached locally in the browser and are not in use until they are required, meaning until the DOM points to them.
The following example is taken from a Google article and shows that preloading JavaScript that is loaded in advance is done slightly differently. You need to set the src
attribute of the file and add it to the DOM:
<link rel="preload" href="used-later.js" as="script">
<!-- ... -->
<script>
var usedLaterScript = document.createElement('script');
usedLaterScript.src = 'used-later.js';
document.body.appendChild(usedLaterScript);
</script>
Summary
Loading assets from the head provides you with more control over how assets are loaded and potentially improves the site’s speed. At Savvy Blog, for example, I load fonts in advance using preload.
Be aware that you should not use preload excessively. If you preload all assets, don’t expect your site to load faster; it might even slow down. In this case, you prevent the browser from scheduling asset loading wisely.
Also, do not confuse preload with prefetch. Do not use preload if you do not need the asset immediately after the page loads. If you need it later, as discussed in the next page, or if the asset is not crucial for displaying the page (such as the analytics script), you should use prefetch or another resource hint…
Lastly, pay attention to browsers that do not support this feature. In such cases, you may want to use a polyfill like this one.