Hello Sunil
priority-hints-fetchpriority-feature-image

Speed Up Resource Loading with Priority Hints and fetchpriority

Loading experience is crucial to the user’s first impression and overall usability, so Google defined Largest Contentful Paint (LCP) metric to measure how quickly the main content loads and is displayed to the user.

The main content for LCP is usually the largest element located above the fold. This element could be an image, video, or simply, just a large block of text. Of all those options, it’s safe to assume that text is the best choice for LCP performance because it loads and renders fast.

web.dev’s main content is an image according to LCP

Browsers follow a critical render path to parse the HTML document and its referenced resources (CSS, JS, images, etc.) to display the content on the screen.

Browsers construct a render tree using DOM and CSSOM, and the page renders once all render-blocking resources like CSS, font files, and scripts have been parsed and processed.

Resource Priorities Defaults

Let’s focus on how these resources are requested and downloaded. The HTML document is the first resource to be requested and downloaded, but how do browsers determine what to download next and in which order?

Browsers have a set of predetermined priorities for each resource type, so they can be downloaded in an optimal order. Here is a rough summary:

Priority

Highest:

  • Main resource (usually a HTML document),
  • CSS (early – if requested before any non-preloaded image file) and font files;

High:

  • Script (early – if requested before any non-preloaded image file),
  • Preload,
  • Image (in viewport);

Medium:

  • CSS and Script (late – if requested after a non-preloaded image file).

Low:

• Script (async),
• Media and images,
• SVG document.

Lowest:

• CSS mismatch,
• Prefetch.

These default priorities usually work pretty well, which results in good web performance.

However, as Addy Osmani mentioned in his article on Priority hints, browsers can make pretty good educated guesses about what to fetch next.

But they don’t know your project as well as you do.

So a little fine-tuning might be necessary.

We can use the preload attribute for the HTML link element to optimize loading performance by ensuring that the browser discovers the resource earlier, downloads it, and caches it. However, that doesn’t provide us with more granular control over a particular resource type.

For example, let’s say that we are loading two render-blocking stylesheets. How can we signal to the browser that main.css file is more important than third-party-plugin.css file without resorting to using JavaScript or some other workaround?

The answer is: – fetchpriority

Going back to our previous example, we can use this attribute to signal to the browser that we want to initiate a request and download of main.css at a higher priority than the third-party-plugin.css which is the same render-blocking CSS resource as main.css.

<link rel="stylesheet" href="/path/to/main.css" fetchpriority="high" />
<link rel="stylesheet" href="/path/to/third-party-plugin.css" fetchpriority="low" />

Pretty simple, right?

📣 Resource hints should be applied sparingly

It is important to note that changing the priority of one resource usually comes at the cost of another resource so hints should be applied sparingly.

Marking everything in the document as high-priority will likely make for a worse user experience but correctly tagging a few resources that the browser would otherwise not load optimally can have a huge benefit.

One good example to use: Use fetchpriority=high to load your LCP hero image sooner.

load your LCP hero image sooner by fetchpriority priority hints

Use fetchpriority=high to load your LCP hero image sooner

It’s not recommended to assign fetchpriority to every resource.

Browsers already do a good enough job, so it should be used sparingly for very specific use cases where we want to prioritize requests for improving LCP, prioritize one deferred resource over the other of the same type, prioritize preload requests, etc.

How to Implement Priority Hints – fetchpriority Attribute

You can implement Priority Hints using the fetchpriority HTML attribute.

You can use the attribute with:

  • link
  • img
  • script
  • iframe tags

The fetchpriority attribute accepts one of three values:

  • high: indicating that you consider the resource critical and want the browser to prioritize it.
  • low: signaling that you consider the resource less important and want the browser to deprioritize it.
  • auto: a default value when you don’t have a preference and let the browser decide the appropriate priority.

When to Use Priority Hints (Examples And Use Cases)

There are 8 main scenarios where Priority Hints could help, Let’s see one by one:

Scenarios -1 – load hero image sooner

Hero images inside the viewport start at a low priority. After the layout is complete, Chrome discovers they are in the viewport and boosts their priority.

This usually adds a significant delay to loading the image. Providing the priority hint in markup lets the image start at a high priority and start loading much earlier.

<!-- Increase the priority of this LCP hero image --> 
<img src="hero-image.jpg" fetchpriority="high" alt="Hero">

Scenarios -2 – Signal high priority images on Carousel

You have several above-the-fold images, but all of them need not have the same priority. For example, in an image carousel, only the first visible image needs a higher priority compared to the others.

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

Scenarios -3 – Signal Priority of Asynchronous Scripts

Declaring scripts as async or defer tells the browser to load them asynchronously. However, these scripts are also assigned a “low” priority as per the resource priorities table.

You may want to bump up their priority while ensuring asynchronous download, particularly for any scripts that are critical for the user experience.

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

On other cases;

A browser may treat parser-blocking scripts, async scripts and preloaded scripts differently based on heuristics that work well in a general case but may not behave exactly as a developer desires.

For example, if a browser treats preloaded scripts as high-priority and async scripts as low priority by default, it could be difficult to preload a dependency of an async script and still have them load in the desired order.

For example, if we have a script a.js that imports b.js, it could be useful to preload b.js so the fetch can start in parallel with the loading of a.js. A trivial implementation of preloading the dependency after the main script:

<script src=a.js async></script>
<link rel=preload href=b.js as=script>

may result in b.js being loaded before a.js in a browser where preloaded scripts are given a higher priority than async scripts.

By using priority hints and assigning the same priority to both scripts, they can be loaded in the preferred order and at a priority that makes sense depending on the context of what the script is being used for (say, high priority for user-facing UI or functionality or low-priority for scripts responsible for background work).

<script src=a.js async fetchpriority=high></script>
<link rel=preload href=b.js as=script fetchpriority=high>

Scenarios -4 – Signal Priority of Fetch API Calls

You may use the JavaScript fetch() API to fetch resources or data asynchronously. Fetch is assigned a high priority by the browser.

There may be situations where you do not want all your fetches to be executed with high priority and prefer using different priority hints instead.

This can be helpful when running background API calls and mixing them with API calls that are responding to user input, like with autocomplete. The background API calls can be tagged as low priority, and the interactive API calls marked as high priority.

For example, assume we have an endpoint autocomplete.json that is used to provide type-down autocomplete suggestions as a user types into a text box and another endpoint prefetch.json that is used to download and cache background data that may be used at some point in the future.

Without priority hints, any calls to autocomplete.json will compete equally for priority, server response and bandwidth.

With priority hints, the interactive API calls to autocomplete.json can get prioritized ahead of the background API calls and if the underlying connection protocol supports prioritization, the relative priority can be carried through to the connection layer and server.

Example – 1

...
// Start prefetching content for possible user interaction and offline support
fetch('/api/prefetch.json', { priority: 'low' }).then(/*...*/)
...
// Handle autocomplete of user keypresses
function autocomplete() {
  fetch('/api/autocomplete.json', { priority: 'high' }).then(/*...*/)
}
...

Example – 2

//Critical fetch request for article content 
fetch('/api/articles.json', {priority: 'high'}).then(/*…*/)
 
//Request for related content now reduced in priority
//reducing the opportunity for contention
fetch('/api/related.json', {priority: 'low'}).then(/*….*/)

Few things to keep in mind when using the fetchpriority attribute:

  • fetchpriority doesn’t ensure that a higher-priority resource will be loaded before other (lower-priority) resources of the same type.
  • fetchpriority shouldn’t be used to control the loading order itself.
  • fetchpriority isn’t a mandatory directive, and it cannot force the browser to fetch a resource or prevent it from fetching. It’s up to the browser if it’s going to fetch the resource or not.

❗ Important

Similar to the Resource hints, Priority Hints should be used cautiously, as overuse might result in slowdowns rather than performance improvements.

Example – 3

For example, let’s say we are loading a blog post. We want to prioritize the main content over comments, so we need to pass a priority attribute in fetch options object.

📚 Please Note

The priority value is high by default, so we only need to assign low when we want to reduce the priority of the fetch request.

/* High-priority fetch for post content (default) */
function loadPost() {
  fetch("https://jsonplaceholder.typicode.com/posts/1")
    .then(parseResponse)
    .then(parsePostData)
    .catch(handleError);
}
 
/* Lower-priority fetch for comments (with priority option) */
function loadComments() {
  fetch("https://jsonplaceholder.typicode.com/posts/1/comments", {
    priority: "low"
  })
    .then(parseResponse)
    .then(parseCommentsData)
    .catch(handleError);
}

Output

See the Pen Fetch with priority by Smashing Magazine (@smashingmag) on CodePen.

Scenarios -5–Embedded Iframe Elements

We can assign fetchpriority to iframe elements just like any other resource. However, keep in mind that this attribute only affects the main resource of the iframe and doesn’t apply to the resources within the iframe.

The browser will load resources within the iframe with default priorities. However, we are still delaying them to start later.

<iframe fetchpriority="low" type="text/html" width="640" height="390" src="http://www.youtube.com/embed/..." frameborder="0"></iframe>

Scenarios -6–Picture elements and priority hints

The HTML picture element lets website owners specify possible image files and the browser then picks the most image with the most appropriate file type and resolution.

To use priority hints with picture tags simply add the fetchpriority attribute to the img element inside the picture tag.

<picture>
  <source srcset="/image-small.png" media="(max-width: 600px)">
  <img src="/image.png" fetchpriority="high">
</picture>

Scenarios -7–Background image and priority hints

For example, let’s say you want to preload a background image on the page. By default the image request will still be made with a low priority. The fetchpriority attributes fixes this:

<link rel="preload" as="image" href="/background.webp" fetchpriority="high" />

Scenarios -8–Deferred Stylesheets

We can also use fetchpriority to signal which scripts and stylesheets should have a higher priority when loading.

📚 Please Note

The scripts and stylesheets remain render-blocking if they are not deferred.

<link rel="stylesheet" href="/path/to/main.css" fetchpriority="high" />
<link rel="stylesheet" href="/path/to/third-party-plugin.css" fetchpriority="low" />

Although this configuration won’t affect LCP or some other performance metrics, it showcases how we can use fetchpriority to improve the loading experience by prioritizing one resource over the other within the same type.

📚 Please Note

preload is still required for the early discovery of LCP images included as CSS backgrounds and can be combined with priority hints by including the fetchpriority='high' on the preload, otherwise, it will still start with the default “Low” priority for images.

Frequently asked questions

Further Reading

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

We are sorry that this post was not useful for you!

Let us improve this post!

Tell us how we can improve this post?

Similar articles you may like

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.

Add comment

Stay Updated

Want to be notified when our article is published? Enter your email address below to be the first to know.

Sunil Pradhan

Hi there 👋 I am a front-end developer passionate about cutting-edge, semantic, pixel-perfect design. Writing helps me to understand things better.