Skip navigation
Part IV Chapter 21

Resource Hints

Introduction

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 (<script>, <link>, and <img>).

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: dns-prefetch, preconnect, preload, 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

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

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 preconnect:

  • 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.

Lastly, preconnect is not available for Internet Explorer or Firefox, and using dns-prefetch as a fallback is highly advised.

preload

The 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

The 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 <script> tag.

<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.

Native lazy loading

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.

Resource hints

Based on the HTTP Archive, let’s jump into analyzing the 2020 trends, and compare the data with the previous 2019 dataset.

Hints adoption

More and more web pages are using the main resource hints, and in 2020 we are seeing the adoption remains consistent between desktop & mobile.

Figure 21.1. Adoption of resource hints.

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.

Figure 21.2. Adoption of resource hints 2019 vs 2020.

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 dns-prefetch and preconnect, you must specify the resource to use preload. While dns-prefetch and 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.

Hints per page

Across the board developers are learning how to better use resource hints, and compared to 2019 we’ve seen an improved use of preload, prefetch, and 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.

Figure 21.3. Median number of hints 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.

20,931
Figure 21.4. The most preload hints on a single page.

As we create more and more automation with resource hints, be cautious when dynamically injecting preload hints - or any elements for that matter!

The as attribute

With preload and 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.

With 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%.

Figure 21.5. Mobile as attribute values by year.

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.

The crossorigin attribute

With preload and 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.

16.96%
Figure 21.6. The percent of elements with preload that use crossorigin.

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!

The media attribute

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.

Best practices

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 dns-prefetch and 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">

Implementing a 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 preconnect & 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 preconnect or dns-prefetch to establish early connections to important third-party origins.

19.67%
Figure 21.7. The percent of pages that pass the preconnect Lighthouse audit.

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.

Native Lazy Loading

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 loading=lazy on <iframe> or <img> elements.

4.02%
Figure 21.8. The percent of pages using native lazy loading.

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.

Predictive prefetching

Combining 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.

Predictive prefetching is a technique that uses methods from data analytics and machine learning to provide a data-driven approach to prefetching. Guess.js is a library that has predictive prefetching support for popular frameworks (Angular, Nuxt.js, Gatsby, and Next.js) and you can take advantage of it today. It ranks the possible navigations from a page and prefetches only the JavaScript that is likely to be needed next.

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 Push

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.

75.36%
Figure 21.9. The percent of HTTP/2 Push pages using preload/nopush.

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.

Service Workers

For both preload and 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.

Figure 21.10. Resource hint adoption on Service Worker pages.

For 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.

Future

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

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!">

With preload and 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.

0.77%
Figure 21.11. The percent of Priority Hint adoption on mobile.

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.

16%
Figure 21.12. The percent of mobile resources with a hint that use the low priority.

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.

103 Early Hints in HTTP/2

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.

As of right now you can follow the conversation on Chromium with issues 671310, 1093693, and 1096414.

Conclusion

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.

Author