When building fast and efficient websites, optimizing how resources like images are loaded is crucial. A good understanding of the different stages that a browser goes through to load and render an image can help you make smarter decisions about performance optimizations.
In this post, we'll take a deep dive into the stages a browser follows to load an image and explain how you can optimize each step for better performance.
The Image Loading Pipeline
Before diving into each stage, it's important to understand that the image loading process is a pipeline where each stage must complete before the next can begin. However, certain HTML attributes can control when stages begin or how they're prioritized:
Stage 1: Request / Fetch
The first step in the image loading process is the request phase. Here, the browser needs to find the image resource and send a request to the server to fetch it.
- What happens: The browser sends an HTTP request to the server to fetch the image from the URL provided in the resource's
src
orstyle
attribute. - Goal: Get the image from the server or a cached location.
- Key attribute:
fetchpriority
influences the priority given to this request compared to other resources. - Also affected by:
loading
attribute determines whether this request happens immediately or is deferred until the image approaches the viewport.
Stage 2: Response / Download Complete
Once the server responds to the request, the image starts downloading. The browser fetches the image file, storing it in memory or cache.
- What happens: The browser receives the image file from the server and downloads it to the local cache.
- Goal: Ensure the browser has the complete image file stored and ready to be decoded.
- No direct attribute control: While no HTML attribute directly controls download speed, both
fetchpriority
(from Stage 1) and image optimization (format, compression) significantly impact this stage. - Dependent on: Stage 1 must complete before Stage 2 can begin.
Stage 3: Decoding
Once the browser has the image data, it needs to decode the image file into a format that can be displayed.
- What happens: The browser transforms the compressed image file into pixel data that can be displayed.
- Goal: Convert the raw image file into a format the browser can render.
- Key attribute:
decoding="async"
offloads this process to a background thread. - Dependent on: Stage 2 must complete before Stage 3 can begin.
Stage 4: Rendering
Once the image is decoded, the browser places it in the render tree and starts rendering it on the page.
- What happens: The image is positioned and styled based on CSS and HTML rules, then rendered on screen.
- Goal: Display the image to the user.
- No direct attribute control: While
loading="lazy"
is often discussed in the context of rendering, it actually controls when Stage 1 (Request) begins, not Stage 4 directly. - Dependent on: Stage 3 must complete before Stage 4 can begin.
Stage 5: Repainting (Optional)
In some cases, once the image is rendered, the browser might need to repaint the page to reflect layout changes.
- What happens: The page layout is adjusted to accommodate the newly rendered image.
- Goal: Ensure the layout is stable when images appear.
- Key attributes:
width
,height
, andaspect-ratio
help prevent layout shifts by reserving space before the image loads. - Dependent on: Stage 4 must complete before Stage 5 can begin.
How HTML Attributes Control the Image Loading Pipeline
Now that we understand each stage, let's clarify how key HTML attributes work across multiple stages:
The loading
Attribute
- Values:
"lazy"
or"eager"
(default) - Primary effect: Controls when Stage 1 (Request) begins
- How it works:
loading="lazy"
: Defers Stages 1-5 until the image approaches the viewportloading="eager"
: Begins Stage 1 immediately upon page parsing
Check out examples and read more details about "loading" attribute
Example of an <img>
tag below the fold:
<img src="below-the-fold.jpg" loading="lazy" alt="an image below the fold">
Example of an <img>
tag above the fold, no need to specify loading="eager"
as it's the default value:
<img src="above-the-fold.jpg" alt="a critical image above the fold">
How loading="lazy"
works:
- Stage 1 (Request): The browser sees the
<img>
tag withloading="lazy"
, but defers sending the HTTP request until the image is approaching the viewport (typically when it's within a certain distance from the visible area). - Stage 2 (Download): Since the request is deferred, the download doesn't happen until the request is made. This means the download is also delayed until the image is close to entering the viewport.
- Stages 3-5 (Decoding, Rendering, Repainting): These stages can only begin after the download completes. The browser can't decode what it hasn't downloaded, and can't render what it hasn't decoded.
How loading="eager"
works:
eager
is the default value, the absence of loading
attribute means the image will be loaded eagerly.
- Stage 1 (Request): The browser immediately sends the HTTP request as soon as it parses the
<img>
tag, regardless of where the image is on the page. - Stage 2 (Download): Download begins immediately after the request is made.
- Stages 3-5: These proceed as soon as possible after download completes.
The fetchpriority
Attribute
- Values:
"high"
,"low"
, or"auto"
(default) - Primary effect: Influences resource prioritization during Stages 1-2
- How it works:
fetchpriority="high"
: Gives the image higher priority in download queuefetchpriority="low"
: Gives the image lower priority in download queue
Check out examples and read more details about "featchpriority" attribute
Example of an <img>
tag where the image has a high priority:
<img src="imortant-image.jpg" fetchpriority="high" alt="an important image">
Example of an <img>
tag where the image has a low priority:
<img src="not-important.jpg" fetchpriority="low" alt="a not important image">
Example of an <img>
tag where the image has default priority - browser decides it all. In this case we can skip the fetchpriority="auto" attribute
:
<img src="not-important.jpg" alt="a not important image">
If how the HTML is laid out is out of your control, you can use <link>
tags to assign fetch priority:
<link rel="important-image.jpg" fetchpriority="high" as="image">
The fetchpriority
attribute works differently from loading
as it doesn't defer or delay requests, but instead influences how the browser prioritizes resources during downloading. Here's a detailed explanation:
How fetchpriority
works:
- Stage 1 (Request): When the browser encounters multiple resources (images, scripts, stylesheets), it typically has to make decisions about which to request first or how to allocate bandwidth. The
fetchpriority
attribute directly influences these decisions:fetchpriority="high"
: Signals to the browser that this image should be given higher priority in the request queue compared to other resources with default or low priority.fetchpriority="low"
: Tells the browser that this image is less important and should yield to other resources.fetchpriority="auto"
(default): Lets the browser use its default prioritization algorithm.
- Stage 2 (Download): The priority affects how network bandwidth is allocated:
- Higher priority images may receive more bandwidth allocation
- The browser may choose to download high-priority images before other resources
- Low-priority images might be downloaded with less bandwidth or after other resources
- Stages 3-5 (Decoding, Rendering, Repainting): Unlike
loading
,fetchpriority
doesn't directly affect these stages. However, by influencing when the download completes, it indirectly affects when these subsequent stages can begin.
Key differences from loading
:
loading="lazy"
determines whether to request an image immediately or to defer until laterfetchpriority
determines how important an image is compared to other resources when requests are being made
The decoding
Attribute
- Values:
"async"
,"sync"
, or"auto"
(default) - Primary effect: Controls how Stage 3 (Decoding) impacts page responsiveness
- How it works:
decoding="async"
: Processes image in background threaddecoding="sync"
: Processes image on main thread (may block other operations)
This only controls how the browser handles image decoding. At this point the image has already been downloaded.
Logical Attribute Combinations
Not all attribute combinations make sense. Here are some logical pairings:
- Critical above-fold images:
<img src="hero.jpg" fetchpriority="high" decoding="async" width="800" height="600" alt="Hero image">
- Below-fold content images:
<img src="content.jpg" loading="lazy" decoding="async" width="400" height="300" alt="Content image">
- Low-priority decorative images:
<img src="decoration.jpg" loading="lazy" fetchpriority="low" decoding="async" width="200" height="200" alt="Decorative element">
Optimizing Image Loading for Core Web Vitals
Effectively using these attributes impacts your Core Web Vitals metrics:
-
Largest Contentful Paint (LCP): Use
fetchpriority="high"
for your LCP image (typically your largest above-fold image) to improve loading performance. -
Cumulative Layout Shift (CLS): Always specify
width
andheight
attributes to reserve space and prevent layout shifts when images load. -
Interaction to Next Paint (INP): Use
decoding="async"
to keep the main thread free for handling user interactions.
Conclusion
Understanding both the stages of image loading and how HTML attributes control this pipeline allows for more strategic optimization. Rather than applying attributes randomly, you can now make informed decisions about which attributes to use for each image based on its importance and position on the page.
By properly implementing loading
, fetchpriority
, decoding
, and dimension attributes, you can create websites that load faster, consume less bandwidth, and provide a more responsive user experience.