How to control the loading order of all web resources by using priority hints

When you open your browser’s network tab, you’ll see a ton of activity. Resources are being downloaded, information is being submitted, events are being logged, and so on.

With so much activity, effectively managing the prioritization of this traffic becomes critical. Bandwidth contention is real, and when all requests fire at the same time, some HTTP requests are not as high priority as others. For example, if you had to choose, you might prefer someone’s payment request to complete successfully rather than just an analysis request indicating that they attempted it. And having your main image appear as quickly as possible is undoubtedly more important than rendering your logo at the bottom of the page.

Fortunately, browsers have a growing number of tools to help prioritize all this web activity. These “priority hints” help browsers make fewer assumptions and clearer decisions about which requests should be prioritized when resources are limited.

This is a useful set of tools that, when used well, can have a substantial impact on page performance, including those increasingly important core web metrics. Let’s explore some of them, and some of the scenarios in which they are most helpful.

This is a useful set of tools that, when used well, can have a substantial impact on page performance, including those increasingly important core web metrics. Let’s explore some of them, and some of the scenarios in which they are most helpful.

Resources loaded first

Modern browsers have a well-supported way of telling the browser what resources the current page will ultimately require: . When it is placed in the of a document, the browser is instructed to download it as quickly as possible with “high” priority.

To be fair, preload scanners in browsers are already pretty good at this. Therefore, preloading usually works best with resources that are discovered later – anything that is not loaded directly by your HTML, such as a background image loaded via an inline style attribute. But it also works for anything else that might not be prioritized by the browser as much as you’d like.

For example: By default, Chrome loads fonts with very high priority, but if someone has a slow network connection, it will use fallback fonts and lower that priority.

Consider a font loaded only via the CSS @font-face rule:

@font-face {
 font-family: "Inter Variable";
 src: url("./font.woff2") format("woff2");
}

When loading, this font received the lowest download priority due to a slow network connection, even though it is very important to the visual experience of the page.

2aa20fe0683e555cca0c539312021097.png

But we can override the browser’s decision by preloading the resource:

<head>
    <!-- Other stuff... -->
 <link rel="preload" href="/font.woff2" as="font">
</head>

Now it’s even more popular:

27cc73d402850ba2e3ae48073150e8ec.png

You can use fetchpriority directly on the link tag to explicitly indicate relative priority, which is useful when preloading multiple resources at the same time.

Here’s a hypothetical scenario where you want to preload two fonts, but want to prioritize one over the other:

<link rel="preload" href="./font-1.woff2" as="font" fetchpriority="low" />
<link rel="preload" href="./font-2.woff2" as="font" fetchpriority="high" />

The results of network activity reflect these indications.

331719f6621ed9392705797553f5386b.png

When to use

Typically, preloading is used when resources are not loaded directly by HTML, but are critical to the experience of the page (such as fonts, CSS background images, etc.). When fetching multiple resources of the same type and you know exactly which one is most important, add the fetchpriority attribute.

Prioritize fetch() requests

In my opinion, the Fetch API is one of the best tools for the modern web. It has some nice features compared to XMLHttpRequest, such as the ability to signal priority on outgoing requests.

The most obvious use case that comes to mind is: Analysis requests. When bandwidth is limited and there are multiple requests in progress, the browser determines its own priorities. But we as engineers should know that general analysis requests should take precedence over other requests that are more critical to the purpose of the page. Modern fetch() makes this easy.

Here’s a simple setup where two requests are enqueued at almost the same time:

fetch("http://localhost:8000/pay", {
 method: "POST",
 body: paymentBody,
});

fetch("http://localhost:8000/log", {
 method: "POST",
 body: loggingBody,
});

By default, the browser automatically treats them both as “high” priority:

4ff9e2c97feb37e4c290824cb2a2fde8.png

Now, we need to explicitly tell the browser the priority of each request:

fetch("http://localhost:8000/pay", {
 method: "POST",
 body: paymentBody,
 + priority: "high"
});

fetch("http://localhost:8000/log", {
 method: "POST",
 body: loggingBody,
 + priority: "low"
});

This time, the priorities are different:

dffc8b75370d9873aed29bcfac81e887.png

A possible concern is that “low” priority requests may be lost – if the user leaves the page prematurely, the request may be canceled. This is a real problem. Depending on several factors, closing a tab or moving to the next page may cause an important but relatively low-priority request to be aborted.

Fortunately, fetch() also accepts a keepalive option. When set to true, the browser will complete the request even if the page terminates.

When to use

Indicate the priority of fetch() when you know that multiple requests are executing concurrently, and you know exactly which one is most important (or which one can be safely demoted).

Prioritizerequests

If we don’t do anything special, the browser will try to determine the most important image on the page. To illustrate this, I loaded the following images with a large distance between them so that only one will be displayed at the “header” of the page.

<img src="./cat-1.jpeg" />
<div style="height: 5000px"></div>
<img src="./cat-2.jpeg" />
<div style="height: 5000px"></div>
<img src="./cat-3.jpeg" />

The browser figured out which one was most important, but it took a second. All three are “low” priority when downloading begins. But soon, the one at the head of the page switched to “high” priority.

c546a2fa94e0c806381214aee2e4da4a.jpeg

Things become more predictable when I add the fetchpriority attribute to the first image:

<img src="./cat-1.jpeg" fetchpriority="high" />

Thereafter, cat-1.jpeg is loaded with highest priority from the beginning. While initially confusing, it makes sense. The browser is very good at determining the criticality of resources, but it benefits from explicit instructions. If you know an image is important, make it clear.

By the way, this feature works great with local image lazy loading, which is a very supported feature now.

<img src="./cat-1.jpeg" fetchpriority="high"/>
<div style="height: 5000px"></div>
<img src="./cat-2.jpeg" loading="lazy" />
<div style="height: 5000px"></div>
<img src="./cat-3.jpeg" loading="lazy" />

With this, the browser knows how to load the image and only loads it when appropriate. In my case it wouldn’t even start requesting the off-screen image on initial load. Instead, it waits until they are closer to the viewport.

ae542da612b0eb8b40244a0ba2e872a3.png

When to use

Use explicit fetchpriority for images when you know they are important to the page experience. The main image is a great place to start, and it can even impact a page’s core web metrics – specifically LCP (maximum content draw).

Prioritize