Core Web Vitals Guide to Resource Prioritization

Control which resources load first and which ones wait

Arjen Karel Core Web Vitals Consultant
Arjen Karel - linkedin
Last update: 2026-02-25

Core Web Vitals Guide to Resource Prioritization

The browser's default prioritization engine operates on heuristics: imperfect guesses based on file types and document location. Resources are enqueued based upon when they are discovered by either the preload scanner or the DOM parser.

Last reviewed by Arjen Karel on February 2026

That can become a problem when you consider that network bandwidth and CPU are not unlimited resources. For example: every byte transferred for a low-priority tracking script, while downloading at the same time, competes directly with the bytes needed for your Largest Contentful Paint (LCP).

Now this is not the browser's fault: in the HTML in our example the browser had no way of knowing that it had prioritized the wrong assets, which delayed critical rendering.

You are the one that knows what is important and you control this schedule through two mechanisms: Prioritization (boosting critical signals) and De-prioritization (scheduling non-critical resources until when they are less intrusive).

Browser Heuristic Limitations

Browsers assign priority based on a "computed priority" score. This score derives from the asset type (CSS, Script, Image) and its position in the HTML/DOM. While generally effective for simple documents, this system fails when resources are not recognized early on (by the preload scanner) or the wrong resources are triggered for an early download.

Chrome's Default Priority Levels

Chrome uses five internal priority levels: Highest, High, Medium, Low, and Lowest. Here is how it assigns them by default:

Resource Default priority With fetchpriority="high" With fetchpriority="low"
CSS (in <head>) Highest (already max) High
Script (blocking, in <head>) Highest (already max) Low
Script (async / defer) Low High Lowest
Font (via preload) Highest (already max) n/a
Font (in CSS) High n/a n/a
Image (default) Low High Lowest
Image (first 5 large) Medium High Low
Image (in viewport, after layout) High (already High) n/a

The fetchpriority attribute is supported on <img>, <link>, and <script> elements. It has 93% global browser support (Chrome 102+, Firefox 132+, Safari 17.2+, Edge 102+) and is a progressive enhancement: browsers that do not support it simply ignore it. According to the 2025 Web Almanac, 17% of mobile pages now use fetchpriority="high", but only 0.3% use fetchpriority="low". That means de-prioritization is a massively underused optimization.

The Preload Scanner Limitation

To accelerate discovery, browsers employ a "preload scanner", a lightweight parser that races ahead of the main HTML parser to find resource URLs. This scanner has limitations (that make it fast and effective): It parses only HTML. It cannot see inside CSS files, it does not execute JavaScript, and it does not render (so it cannot determine if resources are visible in the viewport).

As a consequence, any resource referenced in a stylesheet (such as a background image or a web font), injected by a script, or lazy loaded, is skipped until the main parser downloads and processes the entire webpage. This creates a "discovery delay," where the browser is effectively unaware that critical assets exist.

Resource Contention

When the browser discovers assets, it often attempts to download them simultaneously with other pending requests. If an important LCP image competes with a medium-priority script or unimportant images (like social media icons in the footer), they split the available bandwidth. This contention extends the load time for both, pushing the LCP metric into the "Needs Improvement" zone.

Manual Prioritization Strategies

To build a fast rendering path, you must intervene manually. The goal is to maximize bandwidth for the LCP and minimize it for everything else. Across sites monitored by CoreDash, 82% of sites that use fetchpriority="high" on their LCP image pass LCP, compared to 61% of sites that do not.

1. Fix Discovery with Preloading

You must manually expose hidden assets to the preload scanner. By moving critical resources into the HTML <head> using rel="preload", you force the browser to acknowledge them immediately, eliminating the discovery delay. For a complete walkthrough, see our guide to preloading the LCP image.

The Implementation:

<!-- Expose the font to the scanner immediately -->
<link rel="preload" as="font" type="font/woff2" href="/fonts/inter-bold.woff2" crossorigin>

<!-- Expose the LCP background image immediately -->
<link rel="preload" as="image" href="/images/hero-banner.jpg" fetchpriority="high">

For even earlier discovery, consider 103 Early Hints, which sends preload signals to the browser before the HTML response arrives.

2. Override LCP Heuristics

Browsers often assign "Low" or "Medium" priority to images because they do not know the final layout dimensions during the initial fetch. The browser cannot determine if an image is the LCP until after the render tree is built, which is too late.

The Implementation:

Force "High" priority status on the LCP element using fetchpriority="high". This bypasses internal heuristics and puts the image at the front of the download queue. Google Flights improved their LCP from 2.6 seconds to 1.9 seconds (a 0.7 second improvement) by adding this single attribute.

<!-- Force immediate high-priority fetch -->
<img src="hero.jpg" alt="Hero Product" fetchpriority="high">

The worst mistake here is lazy loading the LCP image, which actively de-prioritizes the most important element on the page.

3. De-prioritize Unimportant Images

Freeing up bandwidth is often more effective than boosting priority. You must explicitly delay non-essential assets to clear the network pipe for critical resources.

The Implementation:

  • Below the fold: Use loading="lazy" to defer offscreen images until the user scrolls.
  • Above the fold, secondary: Use fetchpriority="low" for carousel slides or secondary visuals that render initially but are less important than the LCP.
  • Above the fold, visually unimportant: Bypass the preload scanner by using loading="lazy" and assign a low bandwidth priority. Handy for small images like flags or icons that never catch the eye during a first render but might trigger a lot of early bandwidth requests.
<!-- LCP Image: Highest Priority -->
<img src="slide-1.jpg" fetchpriority="high">

<!-- Secondary Carousel Image: Immediate fetch, low bandwidth usage -->
<img src="slide-2.jpg" fetchpriority="low">

<!-- Translation flags: while in the viewport hide them from the preload scanner -->
<img src="dutch-flag.jpg" loading="lazy" fetchpriority="low">

<!-- Off-screen Image: Deferred fetch -->
<img src="footer-promo.jpg" loading="lazy">

4. Control Script Execution

JavaScript blocks the DOM parser. If you use standard <script> tags, the browser halts HTML parsing to download and execute the file. Uncontrolled script execution also directly harms Interaction to Next Paint (INP) by blocking the main thread.

The Implementation:

  • defer: Use for application logic. It downloads in parallel (Low priority) and executes only after the HTML is fully parsed, preserving dependency order.
  • async: Use for independent third-party scripts (like analytics). It downloads in parallel and executes immediately upon completion, disregarding order.
  • async + fetchpriority="high": Use for critical async scripts (like A/B testing) that need fast download without blocking parsing. This boosts download priority from Low to High while keeping execution non-blocking.
  • Inject: Bypasses the preload scanner so it does not compete with early bandwidth. Injected scripts are treated as async.
  • Schedule + Inject: Inject scripts at a later time, for example when the load event has fired.
<!-- Application Logic: Non-blocking, preserves execution order -->
<script src="app.js" defer></script>

<!-- Third-party Consent: Non-blocking, independent execution -->
<script src="consent.js" async></script>

<!-- Critical async: fast download, non-blocking execution -->
<script src="ab-test.js" async fetchpriority="high"></script>

<script>
  /* Inject example analytics */
  const script = document.createElement('script');
  script.src = 'analytics.js';
  script.async = true;
  document.head.appendChild(script);

  /* Inject + schedule example for chat */
  window.addEventListener('load', () => {
    const chatScript = document.createElement('script');
    chatScript.src = 'chat-widget.js';
    document.head.appendChild(chatScript);
  });
</script>

For a comprehensive breakdown of all available techniques, see 16 methods to defer JavaScript and async vs defer JavaScript. For guidance on categorizing scripts by criticality, see JavaScript priority levels.

5. Unblock CSS Rendering

CSS is render-blocking by design: the browser does not know what the page looks like without CSS. So it downloads and parses the stylesheets first.

Optimization Strategies:

  • Avoid @import: It creates sequential dependency chains that devastate performance.
  • Optimize Bundle Size: Avoid CSS files smaller than 3kB (overhead) and larger than 20kB (blocking). Ideally, target ~15kB files.
  • Async Loading: Load off-screen styles asynchronously to unblock the critical path.
  • Critical CSS Trade-off: While inlining Critical CSS improves the first pageview, it bypasses the browser cache, which can delay subsequent pageviews.

The Implementation:

Eliminate @import entirely. Use <link> tags for parallel loading. For non-critical CSS (like print styles), use the media attribute to unblock the main thread. For more on CSS optimization, see removing unused CSS.

<!-- Critical CSS: Blocks rendering (Correct) -->
<link rel="stylesheet" href="main.css">

<!-- Print CSS: Non-blocking until print event occurs -->
<link rel="stylesheet" href="print.css" media="print">

<!-- Async Pattern: Loads with low priority, applies on load -->
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">

6. Stabilize Font Rendering

Fonts are heavy blocking resources. Effective prioritization requires strict limits on what is downloaded and control over how it renders.

Optimization Strategies:

  • Strict Preload Limits: Preload only the 1-2 most important font files (usually LCP text). Preloading 5+ fonts clogs the bandwidth.
  • Self-host: Self-host your web fonts instead of loading them from a third-party CDN. This eliminates the extra connection setup.
  • Reduce Payload: Use Variable Fonts (one file for all weights) and Subsetting (remove unused characters) to minimize file size.
  • Rendering Strategy:

The Implementation:

<!-- Preload ONLY the critical subset (e.g. Header + Body) -->
<link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin>

<style>
  @font-face {
    font-family: 'Inter Variable';
    src: url('/fonts/inter-var.woff2') format('woff2-variations');
    /* Choose based on stability requirements: */
    font-display: optional; /* No layout shift, but font might stay fallback */
    /* font-display: swap;     Fastest text visibility, but risks layout shift */
  }
</style>

7. Speed Up Connections

Before a browser can download a resource from a third-party origin, it needs to perform a DNS lookup, establish a TCP connection, and negotiate TLS encryption. This takes time. You can eliminate this delay by telling the browser to set up connections early.

The Implementation:

  • preconnect: Use for critical third-party origins that you know the browser will need. It performs the full handshake (DNS + TCP + TLS) ahead of time.
  • dns-prefetch: Use for less critical origins. It only performs the DNS resolution, which is lighter but still saves 20 to 120ms.
<!-- Critical third-party: full early connection -->
<link rel="preconnect" href="https://cdn.example.com">

<!-- Fallback for older browsers -->
<link rel="dns-prefetch" href="https://cdn.example.com">

Limit preconnect to 2 to 4 origins. Each connection consumes CPU and bandwidth. Too many preconnects compete with actual resource downloads and can hurt performance instead of helping it. According to the 2025 Web Almanac, 22% of pages use preconnect and 24% use dns-prefetch. Place both as early as possible in the <head>, before any blocking resources.

About the author

Arjen Karel is a web performance consultant and the creator of CoreDash, a Real User Monitoring platform that tracks Core Web Vitals data across hundreds of sites. He also built the Core Web Vitals Visualizer Chrome extension. He has helped clients achieve passing Core Web Vitals scores on over 925,000 mobile URLs.

Your Lighthouse score is not the full picture.

Lab tests run on fast hardware with a stable connection. I analyze what your actual visitors experience on real devices and real networks.

Analyze Field Data
Core Web Vitals Guide to Resource PrioritizationCore Web Vitals Core Web Vitals Guide to Resource Prioritization