Optimize resource loading with the Fetch Priority API (original) (raw)

The Fetch Priority API indicates the relative priority of resources to the browser. It can enable optimal loading and improve Core Web Vitals.

When a browser parses a web page and begins to discover and download resources such as images, scripts, or CSS, it assigns them a fetch priority so it can download them in an optimal order. A resource's priority usually depends on what it is and where it is in the document. For example, in-viewport images might have a High priority, and the priority for early loaded, render-blocking CSS using <link>s in the <head> might be Very High. Browsers are pretty good at assigning priorities that work well but may not be optimal in all cases.

This page discusses the Fetch Priority API and the fetchpriority HTML attribute, which lets you hint at the relative priority of a resource (high or low). Fetch Priority can help optimize the Core Web Vitals.

Summary

A few key areas where Fetch Priority can help:

A filmstrip view comparing two tests of the Google Flights homepage. At the bottom, Fetch Priority is used to boost the priority of the hero image, resulting in a 0.7 second decrease in LCP.

Fetch Priority improving Largest Contentful Paint from 2.6 s to 1.9 s in a test of Google Flights.

Historically, developers have had limited influence over resource priority using preload and preconnect. Preload lets you tell the browser about critical resources you want to load early before the browser would naturally discover them. This is especially useful for resources that are harder to discover, such as fonts included in stylesheets, background images, or resources loaded from a script. Preconnect helps warm up connections to cross-origin servers and can help improve metrics like Time to first byte. It's useful when you know an origin but not necessarily the exact URL of a resource that will be needed.

Fetch Priority complements these Resource Hints. It's a markup-based signal available through the fetchpriority attribute that developers can use to indicate the relative priority of a particular resource. You can also use these hints through JavaScript and the Fetch API with the priority property to influence the priority of resource fetches made for data. Fetch Priority can also complement preload. Take a Largest Contentful Paint image, which, when preloaded, will still get a low priority. If it is pushed back by other early low-priority resources, using Fetch Priority can help how soon the image gets loaded.

The resource download sequence depends on the browser's assigned priority for every resource on the page. The factors that can affect priority computation logic include the following:

The following table shows how Chrome prioritizes and sequences most resources:

| | Load in layout-blocking phase | Load one-at-a-time in layout-blocking phase | | | | | | ---------------------------------------------- | ------------------------------------------- | -------------------------- | ---------- | ------- | ----------- | | BlinkPriority | VeryHigh | High | Medium | Low | VeryLow | | DevToolsPriority | Highest | High | Medium | Low | Lowest | | Main resource | | | | | | | CSS (early**) | CSS (late**) | CSS (media mismatch***) | | | | | Script (early** or not from preload scanner) | Script (late**) | Script (async) | | | | | Font | Font (rel=preload) | | | | | | Import | | | | | | | Image (in viewport) | Image (first 5 images > 10,000px2) | Image | | | | | Media (video/audio) | | | | | | | Prefetch | | | | | | | XSL | | | | | | | XHR (sync) | XHR/fetch* (async) | | | | |

The browser downloads resources with the same computed priority in the order they're discovered. You can check the priority assigned to different resources when loading a page under the Chrome Dev Tools Network tab. (Make sure you include the priority column by right-clicking the table headings and ticking that).

Network tab of Chrome's DevTools listing a number of font resources. They are all Highest priority.

Priority for resource type = "font" on BBC news detail page

Network tab of Chrome's DevTools listing a number of font resources. They are a mix of Low and High priority.

Priority for resource type = "script" on BBC news detail page.

When priorities change, you can see both the initial and final priority in the Big request rows setting or in a tooltip.

Network tab of Chrome's DevTools. The 'Big request rows' setting is ticked and the Priority column shows the first image with a prioruty of High, and a different initial priority of medium below. The same is shown in the tooltip.

Priority changes in DevTools.

When might you need Fetch Priority?

Now that you understand the browser's prioritization logic, you can tweak your page's download order to optimize its performance and Core Web Vitals. Here are some examples of things you can change to influence the priority of resource downloads:

These techniques help to control the browser's priority computation, thereby improving performance and Core Web Vitals. For example, when a critical background image is preloaded, it can be discovered much earlier, improving the Largest Contentful Paint (LCP).

Sometimes these handles may not be enough to prioritize resources optimally for your application. Here are some of the scenarios where Fetch Priority could be helpful:

The fetchpriority attribute

Use the fetchpriority HTML attribute to specify download priority for resource types such as CSS, fonts, scripts, and images when downloaded using link, img, or script tags. It can take the following values:

Here are a few examples of using the fetchpriority attribute in markup, as well as the script-equivalent priority property.

<!-- We don't want a high priority for this above-the-fold image -->
<img src="/images/in_viewport_but_not_important.svg" fetchpriority="low" alt="I'm an unimportant image!">

<!-- We want to initiate an early fetch for a resource, but also deprioritize it -->
<link rel="preload" href="/js/script.js" as="script" fetchpriority="low">

<script>
  fetch('https://example.com/', {priority: 'low'})
  .then(data => {
    // Trigger a low priority fetch
  });
</script>

Effects of browser priority and fetchpriority

You can apply the fetchpriority attribute to different resources as shown in the following table to increase or reduce their computed priority. fetchpriority="auto" (◉) in each row marks the default priority for that type of resource. (also available as a Google Doc).

| | Load in layout-blocking phase | Load one at a time in layout-blocking phase | | | | | | ---------------------------------------------- | ------------------------------------------- | -------- | ---------- | ------- | ----------- | | BlinkPriority | VeryHigh | High | Medium | Low | VeryLow | | DevToolsPriority | Highest | High | Medium | Low | Lowest | | Main Resource | ◉ | | | | | | CSS (early**) | ⬆◉ | ⬇ | | | | | CSS (late**) | ⬆ | ◉ | ⬇ | | | | CSS (media mismatch***) | ⬆*** | ◉⬇ | | | | | Script (early** or not from preload scanner) | ⬆◉ | ⬇ | | | | | Script (late**) | ⬆ | ◉ | ⬇ | | | | Script (async/defer) | ⬆ | ◉⬇ | | | | | Font | ◉ | | | | | | Font (rel=preload) | ⬆◉ | ⬇ | | | | | Import | ◉ | | | | | | Image (in viewport - after layout) | ⬆◉ | ⬇ | | | | | Image (first 5 images > 10,000px2) | ⬆ | ◉ | ⬇ | | | | Image | ⬆ | ◉⬇ | | | | | Media (video/audio) | ◉ | | | | | | XHR (sync) - deprecated | ◉ | | | | | | XHR/fetch* (async) | ⬆◉ | ⬇ | | | | | Prefetch | ◉ | | | | | | XSL | ◉ | | | | |

fetchpriority sets relative priority, meaning it raises or lowers the default priority by an appropriate amount, rather instead of explicitly setting the priority to High or Low. This often results in High or Low priority, but not always. For example, critical CSS with fetchpriority="high" retains the "Very High"/"Highest" priority, and using fetchpriority="low" on these elements retains the "High" priority. Neither of these cases involve explicitly setting priority to High or Low.

Use cases

Use the fetchpriority attribute when you want to give the browser an extra hint about what priority to fetch a resource with.

Increase the priority of the LCP image

You can specify fetchpriority="high" to boost the priority of the LCP or other critical images.

<img src="lcp-image.jpg" fetchpriority="high">

The following comparison shows the Google Flights page with an LCP background image loaded with and without Fetch Priority. With the priority set to high, the LCP improved from 2.6s to 1.9s.

An experiment conducted using Cloudflare workers to rewrite the Google Flights page using Fetch Priority.

Lower the priority of above-the-fold images

Use fetchpriority="low" to lower the priority of above-the-fold images that aren't immediately important, for example offscreen images in an image carousel.

<ul class="carousel">
  <img src="img/carousel-1.jpg" fetchpriority="high">
  <img src="img/carousel-2.jpg" fetchpriority="low">
  <img src="img/carousel-3.jpg" fetchpriority="low">
  <img src="img/carousel-4.jpg" fetchpriority="low">
</ul>

While images 2-4 will be outside of the viewport, they may be considered "close enough" to boost them to high and also load even if a load=lazy attribute is added. Therefore fetchpriority="low" is the correct solution for this.

In an earlier experiment with the Oodle app, we used this to lower the priority of images that don't appear on load. It decreased page load time by 2 seconds.

A side-by-side comparison of Fetch Priority when used on the Oodle app's image carousel. On the left, the browser sets default priorities for carousel images, but downloads and paints those images around two seconds slower than the example on the right, which sets a higher priority on only the first carousel image.

Using high priority for only the first carousel image lets the page load faster.

Lower the priority of preloaded resources

To stop preloaded resources from competing with other critical resources, you can reduce their priority. Use this technique with images, scripts, and CSS.

<!-- Lower priority only for non-critical preloaded scripts -->
<link rel="preload" as="script" href="critical-script.js">
<link rel="preload" as="script" href="non-critical-script.js" fetchpriority="low">

<!-- Preload CSS without blocking render, or other resources -->
<link rel="preload" as="style" href="theme.css" fetchpriority="low" onload="this.rel='stylesheet'">

Reprioritize scripts

Scripts your page needs to be interactive should be load quickly, but shouldn't block other, more critical, render-blocking resources. You can mark these as async with high priority.

<script src="async_but_important.js" async fetchpriority="high"></script>

You can't mark a script as async if it relies on specific DOM states. However, if they run later on the page, you can load them with lower priority:

<script src="blocking_but_unimportant.js" fetchpriority="low"></script>

This will still block the parser when it reaches this script, but will allow content before this to be prioritised.

An alternative, if the completed DOM is needed, is to use the defer attribute (which runs, in order, after DOMContentLoaded), or even async at the bottom of the page.

Lower the priority for non-critical data fetches

The browser executes fetch with a high priority. If you have multiple fetches that might fire simultaneously, you can use the high default priority for the more important data fetches and lower the priority of less critical data.

// Important validation data (high by default)
let authenticate = await fetch('/user');

// Less important content data (suggested low)
let suggestedContent = await fetch('/content/suggested', {priority: 'low'});

Fetch Priority implementation notes

Fetch Priority can improve performance in specific use cases but there are some things to be aware of when using Fetch Priority:

Developers should use preload for its intended purpose—to preload resources not detected by the parser (fonts, imports, background LCP images). The placement of the preload hint will affect when the resource is preloaded.

Fetch priority is about how the resource should be fetched when it is fetched.

Tips for using preloads

Keep the following in mind when using preloads:

History

Fetch Priority was first experimented with in Chrome as an origin trial in 2018, and then again in 2021 using the importance attribute. At that time it was called Priority Hints. The interface has since changed to fetchpriority for HTML and priority for JavaScript's Fetch API as part of the web standards process. To reduce confusion, we now call this API Fetch Priority.

Conclusion

Developers are likely to be interested in Fetch Priority with the fixes in preload behavior and the recent focus on Core Web Vitals and LCP. They now have additional knobs available to achieve their preferred loading sequence.