Resource Hints — telling the browser the future

The four classic hints, where fetchpriority and the Speculation Rules API fit, and how to defend each choice on a whiteboard at Staff/Lead level.

1One mental model

Every resource has a discovery moment — when the browser learns it needs it (usually mid-parse, or after JS runs). A resource hint moves work earlier than discovery, spending bandwidth/CPU now on a bet that you'll need the resource soon.

That framing gives you the entire trade-off in one sentence — say this in the room:

One-liner

“A hint is a bet on what you'll need next. Bet right → faster. Bet wrong → wasted bandwidth. So only bet on sure things.”

There are two distinct jobs, and conflating them is the classic mistake:

2The family, one card each

dns-prefetch

Just the DNS lookup · cheapest

Resolves a cross-origin hostname ahead of time. Tiny cost, wide support. Use it as the fallback for many third-party origins you might touch.

preconnect

DNS + TCP + TLS · expensive

Opens a full warm connection. Powerful but each one holds a socket — reserve for the 2–4 most critical cross-origins (e.g. your image CDN, fonts host).

preload

Fetch a current-page resource early

Forces an early, high-ish priority fetch for something the parser discovers late — a font in CSS, a hero image, a critical async chunk.

prefetch

Fetch a next-page resource, idle priority

Low-priority fetch into cache for a likely future navigation. Now largely superseded by the Speculation Rules API (§4).

Hint What it does Priority For
dns-prefetch DNS resolution only lowest this page, future origins
preconnect DNS + TCP + TLS handshake n/a (connection) this page, critical origins
preload Fetch a known resource now High / as-typed this page
modulepreload Fetch + parse an ES module graph High this page (JS)
prefetch Fetch for future use, cache it Lowest (idle) next page
Speculation Rules Prefetch or full prerender of a URL tunable “eagerness” next page

Sources: web.dev — resource hints · MDN — Speculative loading

3The syntax (and two traps that fail interviews)

<!-- Connection-level: warm the pipe to your image CDN -->
<link rel="preconnect" href="https://images.example.net">
<link rel="dns-prefetch" href="https://images.example.net"> <!-- fallback for old browsers -->

<!-- Content-level: hero image for this page, made truly high priority -->
<link rel="preload" as="image" href="/hero.avif" fetchpriority="high">

<!-- Font: MUST have crossorigin, even same-origin, or it downloads TWICE -->
<link rel="preload" as="font" type="font/woff2"
      href="/inter.woff2" crossorigin>
Trap 1 — as is mandatory on preload. Omit it and the browser can't set the right priority or match the later request, so it fetches the resource a second time. as is what tells it “this is a font / image / script.”
Trap 2 — fonts need crossorigin even same-origin. Fonts are fetched in CORS mode (anonymous). A preload without crossorigin uses a different mode, the cache keys don't match, and you download the font twice — the single most cited preload bug. [web.dev]

fetchpriority — the modern lever

Preloading an image does not make it high priority — images default to low. Add fetchpriority="high" to your LCP hero, and fetchpriority="low" to below-the-fold or non-critical fetches to yield bandwidth. This is the 2024+ answer to “how do you make the LCP image win the race.”

One-liner

“Preload says find this early. fetchpriority says and win the race. For the hero image you need both — preload alone still leaves it at low priority.”

The trap: what if everything is high?

Priority is relative — it only helps by creating contrast. Mark five preloads fetchpriority="high" and you've flattened the hierarchy right back out:

The fix isn't “many highs” — it's one high + demote the rest. Give high to the single LCP resource and push competitors (a gallery, below-the-fold images) to low to clear its lane. The win comes from the contrast, not the label. Exception: a couple of high hints on genuinely non-competing critical resources (LCP image and a text-blocking font) is fine — they're not racing for the same finish line.
One-liner

fetchpriority is a ranking, not a megaphone. If everything's high, nothing is. One high for the LCP, low for its competitors to clear the lane.”

preload as="script" vs modulepreload

For an ES module (<script type="module">), there are two ways to fetch early — and they get the module to very different stages of the pipeline:

preload as="script" modulepreload
Does downloads → HTTP cache (raw bytes) downloads + parses + compiles → module map, ready to run
Dependency graph just that one file can fetch the import graph too
For classic scripts, anything ES modules specifically

ES modules load sequentially by nature: the browser can't discover b.js until it has fetched and parsed a.js and seen the import. modulepreload lets it walk the static imports and fetch them in parallel — flattening that waterfall. The parse/compile is done ahead of time too, so at execution it's near-free.

Two gotchas they'll probe. (1) Modules fetch in CORS mode — modulepreload handles it; using preload as="script" for a module without matching crossorigin gives the double-download bug (same class as the font trap). (2) Auto-fetching dependencies is a browser optimization, not a guarantee — to be safe you modulepreload each module (bundlers like Vite emit these for you). And support: modulepreload is Chromium + Firefox, Safari only since 17.5 (2024) — older Safari ignores it and falls back to preload as="script".
One-liner

preload gets you the bytes; modulepreload gets you the parsed module — and flattens the import waterfall. For ES modules, reach for modulepreload.”

Sources: web.dev — Preload modules · MDN — rel=modulepreload · caniuse — support

4The part that signals you're current: Speculation Rules

Saying “use <link rel=prefetch> for the next page” is a 2019 answer. The current platform direction is the Speculation Rules API, which supersedes <link rel=prerender> and improves on prefetch.

<script type="speculationrules">
{
  "prerender": [{
    "where": { "href_matches": "/hotel/*" },
    "eagerness": "moderate"
  }]
}
</script>

Sources: MDN — Speculation Rules API · Chrome — Prerender pages

5Putting it on the platform's whiteboard

Tie it to their product — a hotel search/listing page:

Full loop

Concept: hints move network work before discovery. Trade-off: every hint competes for the same bandwidth, so over-hinting slows the critical path. Anchor: “We preconnected to 8 origins ‘to be safe’ and regressed LCP; cutting to the 3 that mattered recovered it.” Impact: p75 LCP is a ranking + conversion lever on an SEO-critical site. Invite: “I'd prerender more aggressively on desktop where bandwidth is cheap, conservatively on mobile data.”

6Check yourself — scenario quiz

Pick an answer; you get instant feedback. These mimic the push-back style of the round.

1. You preload a same-origin WOFF2 font with <link rel="preload" as="font" type="font/woff2" href="/inter.woff2">. What happens?

2. Your LCP is a hero image. You add <link rel="preload" as="image"> but LCP barely improves. Best next move?

3. Interviewer: “We have 10 third-party origins the page might call. Should we preconnect them all?” Strongest Lead answer?

They're probing whether you understand cost, not just the API.

4. You want near-instant navigation to the top hotel result on hover. Most current technique?

5. Which is the single best one-sentence framing of the resource-hint trade-off?

6. A teammate marks all six above-the-fold images fetchpriority="high" to “make the page fast.” What's the most likely result?

Classic over-promotion trap — they're testing whether you know priority is relative.

7. You're early-loading an ES module entry point (<script type="module">) that imports several others. Best hint?

0 / 7 answered

Try this aloud before our next session: “Walk me through the resource hints you'd put in the <head> of a hotel search results page, and defend why each one is worth the bandwidth.” Time yourself to 90 seconds.
Good follow-up topics:
“Quiz me out loud, harder” “103 Early Hints vs preload?” “modulepreload vs preload for JS” “Walk a platform listing waterfall” “Where do hints sit in the CRP?”