Getting the load order of critical resources right, not just the total load time
Load time alone says little about whether a page feels fast to a visitor, because two pages with identical total load time can feel completely different depending on the order in which their resources arrive. The fetchpriority attribute, , and the browser's own preload scanner each intervene at a different point in that order, and only together do they reach their full effect. The most common mistake is preloading as many resources as possible without considering that every additional preload directive competes for the same limited bandwidth as every other critical resource on the page.
Table of Contents
- 1. Why load order matters as much as load time
- 2. The fetchpriority attribute in detail
- 3. The browser's own preload scanner: what it finds automatically
- 4. Using link rel preload deliberately
- 5. How fetchpriority, preload, and the preload scanner work together
- 6. The common mistake: too many preload directives competing for bandwidth
- 7. Understanding the browser's internal priority levels
- 8. A systematic approach: identifying the most critical resources first
- 9. Measuring the effect in the network panel and Real User Monitoring
- 10. Summary
- 11. FAQ
1. Why load order matters as much as load time
Two pages with identical total load time can feel completely different to a visitor if they differ in the order their resources arrive. If the large hero image loads first and the critical CSS only afterward, the page feels empty for a long stretch, even though objectively the same number of bytes were transferred as on a page that loads CSS and fonts first and deliberately delays the hero image a little. Load order is therefore not a side issue but its own optimization lever, independent of raw file size or request count.
Browsers already make a rough priority decision without any developer input at all, by weighting resource types like CSS and fonts higher by default than, say, images below the fold. This default heuristic is a reasonable starting point for many pages, but it does not always land on the actually most important resource, for instance when the large hero image also happens to be the LCP element and would therefore matter more than an image far down the page that the browser treats technically identically. This is exactly where the three mechanisms described below come in.
2. The fetchpriority attribute in detail
The fetchpriority attribute lets you set one of three levels directly on an HTML element: high, low, or auto, where auto leaves the browser's default heuristic untouched. It applies to img, link, script, and iframe elements and overrides the automatic classification specifically for that one element, without any global effect on other resources. For a page's LCP image, fetchpriority="high" is one of the most effective single-line changes available, since it explicitly tells the browser to load that image ahead of other resources at the same default priority.
Just as important is the opposite case: fetchpriority="low" tells the browser to deliberately downgrade a resource, for example images that only become visible far down the page but cannot be delayed with loading="lazy" for technical reasons. The example below shows both cases together in a typical page head.
<!-- Explicitly prioritize the LCP image -->
<img
src="/images/hero-product.avif"
fetchpriority="high"
width="1200"
height="600"
alt="Hero product image"
/>
<!-- Deliberately deprioritize a visible but non-critical image -->
<img
src="/images/trust-badges.avif"
fetchpriority="low"
loading="eager"
width="400"
height="80"
alt="Trust badges"
/>
3. The browser's own preload scanner: what it finds automatically
The preload scanner is a separate, very fast parser that modern browsers run in parallel with the main HTML parser, to detect obvious resources like img tags, link elements, or script src attributes early and kick off their download before the main parser, let alone JavaScript, has even reached that point in the document. For most images and resources statically linked in the HTML, this mechanism alone is enough to guarantee an early load, with no extra preload directive needed.
The preload scanner's limit lies with resources that only become known through JavaScript execution, for example a background image set via CSS-in-JS, or a font that is only referenced after a particular component loads. These hidden resources escape the preload scanner entirely and are consequently requested noticeably later, which is exactly where an explicit preload directive makes the biggest difference.
4. Using link rel preload deliberately
explicitly instructs the browser to load a resource as early as possible, regardless of whether the preload scanner would have found it anyway. For resources that escape the scanner, for example a critical web font only referenced through a CSS @font-face rule, or a background image loaded via JavaScript, preload is often the only way to force an early download at all. It matters to set the as attribute correctly, for example as="font" with a matching crossorigin attribute, since otherwise the browser may end up loading the resource a second time.
A common use case is the critical, above-the-fold web font that, without preload, is only requested after the CSS has been parsed and applied, even though it is already needed for the first visible text. A targeted preload of that single font file noticeably shortens the time until final, unswapped text, but should be deliberately limited to font variants that are genuinely above the fold, rather than blanket preloading every font variant used in the project.
5. How fetchpriority, preload, and the preload scanner work together
The three mechanisms operate at different levels and complement each other rather than replacing one another: the preload scanner ensures resources visible in the HTML get discovered early in the first place, preload forces early discovery even for hidden resources or ones only known through JavaScript, and fetchpriority fine-tunes, within that already-early set of requests, which of them actually get transferred first when the available bandwidth cannot serve them all at once.
A well-tuned setup for a typical product page therefore combines all three levels: the hero image gets discovered early by the preload scanner anyway and additionally gets favored via fetchpriority="high", a critical web font gets forced via preload since it would otherwise escape the scanner, and secondary images that happen to sit early in the HTML get deliberately downgraded via fetchpriority="low", so they do not compete for bandwidth against the critical image-and-font combination.
6. The common mistake: too many preload directives competing for bandwidth
By far the most common mistake when introducing preload strategies is preloading too many resources at once, on the assumption that more preloads can only help. In reality every preload directive competes for the same limited bandwidth of the initial connection, and every additional preloaded resource that is not actually critical indirectly slows down the resources that genuinely matter, because they all have to share the same connection slots and available bandwidth.
In practice a generously applied preload list with ten or more entries almost always causes the actual LCP element to arrive slower despite its own preload than it would without the extra competition, since the browser has to split limited bandwidth across all preloaded resources at once. The rule should therefore be to use preload exclusively for resources that provably escape the preload scanner and are genuinely needed for the first visible page content, rather than treating it as a generic performance tool to sprinkle over arbitrary resources.
7. Understanding the browser's internal priority levels
Internally browsers work with a finer gradation than the three visible fetchpriority values, typically five levels ranging from Highest through High, Medium, Low, down to Lowest, which can be inspected directly in Chromium's network panel under the Priority column. Resource types like render-blocking CSS get a high priority by default, while images below the viewport typically start at Low, and it is precisely this internal classification that fetchpriority shifts, without changing the underlying five-level logic itself.
Understanding these internal levels helps apply fetchpriority not blindly but specifically where a resource actually starts at a lower level than its importance to the visitor would justify. An image the preload scanner already classifies at high priority because it sits early in the HTML gains almost nothing from an additional fetchpriority="high", while the exact same directive makes the decisive difference for an image that actually starts at a low default priority but is critical for LCP.
8. A systematic approach: identifying the most critical resources first
Rather than distributing priority hints intuitively, a systematic process pays off: first identify the page's actual LCP element, usually via the PerformanceObserver API or a Lighthouse audit, and give fetchpriority="high" exclusively to that element. Second, list every resource needed for the first visible viewport that is provably missed by the preload scanner, for example assets loaded via CSS-in-JS or a late JavaScript import, and give only those a targeted preload.
Third, identify resources that sit early in the HTML and therefore get automatically prioritized high, but are actually irrelevant to the first viewport, for example images inside a hidden tab panel, and deliberately give those fetchpriority="low" to free up bandwidth for the genuinely critical resources. This three-step approach ensures every priority directive applied has a justified, measurable effect, instead of being sprinkled across as many elements as possible as a blanket precaution.
9. Measuring the effect in the network panel and Real User Monitoring
The effect of a priority hints strategy can be checked directly in the browser DevTools network panel, where the Priority column shows the actually assigned internal level for every resource, immediately revealing whether a fetchpriority directive actually took effect. The order in the waterfall diagram additionally shows whether prioritized resources are actually requested earlier than before, or whether other factors like a limited number of parallel connections still prevent the desired order.
For production validation, the Element Timing API combined with Real User Monitoring delivers the most reliable data, since lab measurements under ideal network conditions tend to underestimate the effect of prioritization, while it turns out considerably stronger under the real, often constrained bandwidth conditions of mobile visitors. A regular check after every deployment also ensures that new resources, say an added marketing script, do not silently push ahead of the deliberately prioritized, critical resources.
| Mechanism | Level of effect | Typical use case | Common mistake |
|---|---|---|---|
| fetchpriority="high" | Single element, shifts priority level | LCP image or critical script | Applied to too many elements at once, loses its effect |
| fetchpriority="low" | Single element, deliberately downgrades | Visible but non-critical images | Rarely used deliberately, even though it frees bandwidth for critical resources |
| link rel preload | Forces early discovery | Web fonts, assets referenced via JS | Too many simultaneous preloads compete for bandwidth |
| Preload scanner | Automatic, no developer input needed | Statically linked images and scripts in the HTML | Does not detect resources only created through JavaScript |
Mironsoft
Web performance, Core Web Vitals, and load time optimization
Load times that don't make users bounce before the page is even visible?
We review existing websites for slow Core Web Vitals, bloated JavaScript bundles, and unnecessary render blockers, then build a performance foundation that stays measurable instead of just looking good once.
Performance Audit
Systematically measuring and fixing Core Web Vitals, load waterfall, and render blockers.
Bundle Optimization
Specifically reducing JavaScript and CSS bundle size and improving code splitting.
Monitoring Setup
Establishing continuous performance monitoring instead of a one-time snapshot.
10. Summary
Priority Hints and Preload: The Key Points
Core idea
Load order is its own performance lever, independent of file size or request count.
fetchpriority
Shifts individual elements up or down within the priority levels the browser has already assigned.
Most common mistake
Too many preload directives compete for the same limited bandwidth and slow down the resources that actually matter.
Approach
Identify the LCP element first, preload hidden but critical resources next, then downgrade secondary resources last.