Over the past decade resource hints have become essential primitives that allow developers to improve page performance and therefore the user experience.
Preloading resources and having browsers apply some intelligent prioritization is something that was actually started way back in 2009 by IE8 with something called the preloader. In addition to the HTML parser, IE8 had a lightweight look-ahead preloader that scanned for tags that could initiate network requests (
Over the following years, browser vendors did more and more of the heavy lifting, each adding their own special sauce for how to prioritize resources. But it’s important to understand that the browser alone has some limitations. As developers however, we can overcome these limits by making good use of resource hints and help decide how to prioritize resources, determining which should be fetched or preprocessed to further boost page performance.
In particular we can mention a few of the victories resource hints achieved/made in the last year:
- CSS-Tricks web fonts showing up faster on a 3G first render.
- Wix.com using resource hints got 10% improvement for FCP.
- Ironmongerydirect.co.uk used preconnect to improve product image loading by 400ms at the median and greater than 1s at the 95th percentile.
- Facebook.com used preload for faster navigation.
Let’s take a look at most predominant resource hints supported by most browsers today:
prefetch, and native lazy loading.
When working with each individual hint we advise to always measure the impact before and after in the field, by using libraries like WebVitals, Perfume.js, or any other utility that supports the Web Vitals metrics.
dns-prefetch helps resolve the IP address for a given domain ahead of time. As the oldest resource hint available, it uses minimal CPU and network resources compared to
preconnect, and helps the browser to avoid experiencing the “worst-case” delay for DNS resolution, which can be over 1 second.
<link rel="dns-prefetch" href="https://www.googletagmanager.com/">
Be mindful when using
dns-prefetch as even if they are lightweight to do it’s easy to exhaust browser limits for the number of concurrent in-flight DNS requests allowed (Chrome still has a limit of 6).
preconnect helps resolve the IP address and open a TCP/TLS connection for a given domain ahead of time. Similar to
dns-prefetch it is used for any cross-origin domain and helps the browser to warm up any resources used during the initial page load.
<link rel="preconnect" href="https://www.googletagmanager.com/">
Be mindful when you use
- Only warm up the most frequent and significant resources.
- Avoid warming up origins used too late in the initial load.
- Use it for no more than three origins because it can have CPU and battery cost.
preload hint initiates an early request. This is useful for loading important resources that would otherwise be discovered late by the parser.
<link rel="preload" href="style.css" as="style"> <link rel="preload" href="main.js" as="script">
Be mindful of what you are going to
preload, because it can delay the download of other resources, so use it only for what is most critical to help you improve the Largest Contentful Paint (LCP). Also, when used on Chrome, it tends to over-prioritize
preload resources and potentially dispatches preloads before other critical resources.
Lastly, if used in a HTTP response header, some CDN’s will also automatically turn a
preload into a HTTP/2 push which can over-push cached resources.
prefetch hint allows us to initiate low-priority requests we expect to be used on the next navigation. The hint will download the resources and drop it into the HTTP cache for later usage. Important to notice,
prefetch will not execute or otherwise process the resource, and to execute it the page will still need to call the resource by the
<link rel="prefetch" as="script" href="next-page.bundle.js">
There are a variety of ways to implement a resource’s prediction logic, it could be based on signals like user mouse movement, common user flows/journeys, or even based on a combination of both on top of machine learning.
Be mindful, depending on the quality of HTTP/2 prioritization of the CDN used,
prefetch prioritization could either improve performance or make it slower, by over prioritizing
prefetch requests and taking away important bandwidth for the initial load. Make sure to double check the CDN you are using and adapt to take into consideration some of the best practices shared in Andy Davies’s article.
The native lazy loading hint is a native browser API for deferring the load of offscreen images and iframes. By using it, assets that are not needed during the initial page load will not initiate a network request, this will reduce data consumption and improve page performance.
<img src="image.png" loading="lazy" alt="…" width="200" height="200">
Be mindful Chromium’s implementation of lazy-loading thresholds logic historically has been quite conservative, keeping the offscreen limit to 3000px. During the last year the limit has been actively tested and improved on to better align developer expectations, and ultimately moving the thresholds to 1250px. Also, there is no standard across the browsers and no ability for web developers to override the default thresholds provided by the browsers, yet.
Based on the HTTP Archive, let’s jump into analyzing the 2020 trends, and compare the data with the previous 2019 dataset.
More and more web pages are using the main resource hints, and in 2020 we are seeing the adoption remains consistent between desktop & mobile.
The relative popularity of
dns-prefetch with 33% adoption compared with other resource hints is unsurprising as it first appeared in 2009, and has the widest support out of all major resource hints.
Compared to 2019 the
dns-prefetch had a 4% increase in Desktop adoption. We saw a similar increase for
preconnect as well. One key reason this was the largest growth between all hints, is the clear and useful advice the Lighthouse audit is giving on this matter. Starting from this year’s report we also introduce how the latest dataset performs against Lighthouse recommendations.
preload usage has had a slower growth with only a 2% increase from 2019. This could be in part because it requires a bit more specification. While you only need the domain to use
preconnect, you must specify the resource to use
preconnect are reasonably low risk, though still can be abused,
preload has a much greater potential to actually damage performance if used incorrectly.
prefetch is used by 3% of sites on Desktop, making it the least widely used resource hint. This low usage may be explained by the fact that
prefetch is useful for improving subsequent, rather than current, page loads. Thus, it will be overlooked if a site is only focused on improving its landing page, or the performance of the first page viewed. In the coming years with a more clear definition on what to measure for improving subsequent page experience, it could help teams prioritize
prefetch adoption with clear performance quality goals to reach.
Across the board developers are learning how to better use resource hints, and compared to 2019 we’ve seen an improved use of
preconnect. For expensive operations like preload and preconnect the median usage on desktop decreased from 2 to 1. We have seen the opposite for loading future resources with a lower priority with
prefetch, with an increase from 1 to 2 in median per page.
Resource hints are most effective when they’re used selectively (“when everything is important, nothing is”). Having a more clear definition of what resources help improve critical rendering, versus future navigation optimizations, can move the focus away from using
preconnect and more towards
prefetch by shifting some of the resource prioritization and freeing up bandwidth for what most helps the user at first.
However, this hasn’t stopped some misuse of the
preload hint, since in one instance we discovered a page dynamically adding the hint and causing an infinite loop that created over 20k new preloads.
As we create more and more automation with resource hints, be cautious when dynamically injecting preload hints - or any elements for that matter!
prefetch, it’s crucial to use the
as attribute to help the browser prioritize the resource more accurately. Doing so allows for proper storage in the cache for future requests, applying the correct Content Security Policy (CSP), and setting the correct
Accept request headers.
preload many different content-types can be preloaded and the full list follows the recommendations made in the Fetch spec. The most popular is the
script type with 64% usage. This is likely related to a large group of sites built as Single Page Apps that need the main bundle as soon as possible to start downloading the rest of their JS dependencies. Subsequent usage comes from font at 8%, style at 5%, image at 1%, and fetch at 1%.
Compared to the trend in 2019, we’ve seen rapid growth in font and style usage with the
as attribute. This is likely related to developers increasing the priority of critical CSS and also combining
preload fonts with
display:optional to improve Cumulative Layout Shift (CLS).
Be mindful that omitting the
as attribute, or having an invalid value will make it harder for the browser to determine the correct priority and in some cases, such as scripts, can even cause the resource to be fetched twice.
preconnect resources that have CORS enabled, such as fonts, it’s important to include the
crossorigin attribute, in order for the resource to be properly used. If the
crossorigin attribute is absent, the request will follow the single-origin policy thereby making the use of preload useless.
The latest trends show that 16.96% of elements that
preload also set
crossorigin and load in anonymous (or equivalent) modes, and only 0.02% utilize the
use-credentials case. This rate has increased in conjunction with the increase in font-preloading, as mentioned earlier.
<link rel="preload" href="ComicSans.woff2" as="font" type="font/woff2" crossorigin>
Be mindful that fonts preloaded without the
crossorigin attribute will be fetched twice!
When it’s time to choose a resource for use with different screen sizes, reach for the
media attribute with
preload to optimize your media queries.
<link rel="preload" href="a.css" as="style" media="only screen and (min-width: 768px)"> <link rel="preload" href="b.css" as="style" media="screen and (max-width: 430px)">
Seeing over 2,100 different combinations of media queries in the 2020 dataset encourages us to consider how wide the variance is between concept and implementation of responsive design from site to site. The ever popular
767px/768px breakpoints (as popularized by Bootstrap amongst others) can be seen in the data.
Using resource hints can be confusing at times, so let’s go over some quick best practices to follow based on Lighthouse’s automated audit.
To safely implement
preconnect make sure to have them in separate links tags.
<link rel="preconnect" href="http://example.com"> <link rel="dns-prefetch" href="http://example.com">
dns-prefetch fallback in the same
<link> tag causes a bug in Safari that cancels the
preconnect request. Close to 2% of pages (~40k) reported the issue of both
dns-prefetch in a single resource.
In the case of “Preconnect to required origins” audit, we saw only 19.67% of pages passing the test, creating a large opportunity for thousands of websites to start using
dns-prefetch to establish early connections to important third-party origins.
Lastly, running Lighthouse’s “Preload key requests” audit resulted in 84.6% of pages passing the test. If you are looking to use
preload for the first time, remember, fonts and critical scripts are a good place to start.
Now let’s celebrate the first year of the Native Lazy Loading API, which at the time of publishing already has over 72% browser support. This new API can be used to defer the load of below-the-fold iframes and images on the page until the user scrolls near them. This can reduce data usage, memory usage, and helps speed up above-the-fold content. Opting-in to lazy load is as simple as adding
Adoption is still in its early days, especially with the official thresholds earlier this year being too conservative, and only recently aligning with developer expectations. With almost 72% of browsers supporting native image/source lazy loading, this is another area of opportunity especially for pages looking to improve data usage and performance on low-end devices.
Running Lighthouse’s “Defer offscreen images” audit resulted in 68.65% of pages passing the test. For those pages there is an opportunity to lazy-load images after all critical resources have finished loading.
Be mindful to run the audit on both desktop and mobile as images may move off screen when the viewport changes.
prefetch with machine learning can help improve the performance of subsequent page(s). One solution is Guess.js which made the initial breakthrough in predictive-prefetching, with over a dozen websites already using it in production.
Depending on the training set, the prefetching of Guess.js comes with over 90% accuracy.
Overall, predictive prefetching is still uncharted territory, but combined with prefetching on mouse over and Service Worker prefetching, it has great potential to provide instant experiences for all users of the website, while saving their data.
HTTP/2 has a feature called “server push” that can potentially improve page performance when your product experiences long Round Trip Times(RTTs) or server processing. In brief, rather than waiting for the client to send a request, the server preemptively pushes a resource that it predicts the client will request soon afterwards.
HTTP/2 Push is often initiated through the
preload link header. In the 2020 dataset we have seen 1% of mobile pages using HTTP/2 Push, and of those 75% of preload header links use the
nopush option in the page request. This means that even though a website is using the
preload resource hint, the majority prefer to use just this and disable HTTP/2 pushing of that resource.
It’s important to mention that HTTP/2 Push can also damage performance if not used correctly which probably explains why it is often disabled.
One solution to this, is to use the PRPL Pattern which stands for Push (or preload) the critical resources, Render the initial route as soon as possible, Pre-cache remaining assets, and Lazy-load other routes and non-critical assets. This is possible only if your website is a Progressive Web App and uses a Service Worker to improve the caching strategy. By doing this, all subsequent requests never even go out to the network and so there’s no need to push all the time and we still get the best of both worlds.
prefetch we’ve had an increase in adoption when the page is controlled by a Service Worker. This is because of the potential to both improve the resource prioritization by preloading when the Service Worker is not active yet and intelligently prefetching future resources while letting the Service Worker cache them before they’re needed by the user.
preload on desktop we have an outstanding 47% rate of adoption and
prefetch a 10% rate of adoption. In both cases the data is much higher compared to average adoption without a Service Worker.
As mentioned earlier, the PRPL Pattern will play a significant role in the coming years in how we combine resource hints with the Service Worker caching strategy.
Let’s dive into a couple of experimental hints. Very close to release we have Priority Hints, which is actively experimented with in the web community. We also have the 103 Early Hints in HTTP/2, which is still in early inception and there are a few players like Chrome and Fastly collaborating for upcoming test trials.
Priority hints are an API for expressing the fetch priority of a resource: high, low, or auto. They can be used to help deprioritize images (e.g. inside a Carousel), re-prioritize scripts, and even help de-prioritize fetches.
This new hint can be used either as an HTML tag or by changing the priority of fetch requests via the
importance option, which takes the same values as the HTML attribute.
<!-- We want to initiate an early fetch for a resource, but also deprioritize it --> <link rel="preload" href="/js/script.js" as="script" importance="low"> <!-- An image the browser assigns "High" priority, but we don't actually want that. --> <img src="/img/in_view_but_not_important.svg" importance="low" alt="I'm not important!">
prefetch, the priority is set by the browser depending on the type of resource. By using Priority Hints we can force the browser to change the default option.
So far only 0.77% websites adopted this new hint as Chrome is still actively experimenting, and at the time of this article’s release the feature is on-hold.
The largest use is with script elements, which is unsurprising as the number of JS primary and third-party files continues to grow.
The data shows us that 83% of resources using Priority Hints use a “high” priority on mobile, but something we should pay even more attention to is the 16% of resources with “low” priority.
Priority hints have a clear advantage as a tool to prevent wasteful loading via the “low” priority by helping the browser decide what to de-prioritize and giving back significant CPU and bandwidth to complete critical requests first, rather than as a tactic to try to get resources loaded more quickly with the “high” priority.
Previously we mentioned that HTTP/2 Push could actually cause regression in cases where assets being pushed were already in the browser cache. The 103 Early Hints proposal aims to provide similar benefits promised by HTTP/2 push. With an architecture that is potentially 10x simpler, it addresses the long RTT’s or server processing without suffering from the known worst-case issue of unnecessary round trips with server push.
During the past year resource hints increased in adoption, and they have become essential APIs for developers to have more granular control over many aspects of resource prioritizations and ultimately, user experience. But let’s not forget that these are hints, not instructions and unfortunately the Browser and the network will always have the final say.
Sure, you can slap them on a bunch of elements, and the browser may do what you’re asking it to. Or it may ignore some hints and decide the default priority is the best choice for the given situation. In any case, make sure to have a playbook for how to best use these hints:
- Identify key pages for the user experience.
- Analyze the most important resources to optimize.
- Adopt the PRPL Pattern when possible.
- Measure the performance experience before and after each implementation.
As a final note, let’s remember that the web is for everyone. We must continue to protect it and stay focused on building experiences that are easy and frictionless.
We are thrilled to see that year after year we get incrementally closer to offering all the APIs required to simplify building a great web experience for everyone, and we can’t wait to see what comes next.