Frontend Performance/Loading Less, Sooner
Lesson 14 of 20 · Episode 14

Resource Hints — preload

Tell the browser about a critical resource early so it fetches at high priority instead of late.

preloadPriorityFontsLCP image
Watch on YouTube ↗

preload is for a different problem than preconnect. Preconnect warms a connection; preload fetches an actual resource early — specifically a critical, late-discovered one the preload scanner can't see in the initial HTML. It's the go-to fix for a slow LCP caused by a CSS-referenced image or font.

The problem

Late-discovered resources

When HTML streams in, the preload scanner races ahead and starts fetching anything it can see in the markup. But some critical resources are invisible to it because they're referenced from other files:

preload short-circuits this: you put a hint in the HTML so the scanner fetches the resource immediately, in parallel, instead of waiting for the chain that would normally reveal it.

See it

Preload the LCP image

Here's a hero image referenced from CSS. Without preload it can't start until the stylesheet is parsed; with preload it loads alongside the CSS and is ready before the stylesheet even finishes. Watch the LCP marker jump:

InteractiveA CSS background image, with and without preload
index.html
styles.css
hero.jpg (in CSS)
LCP 1.18s
The hero image is a background-image in CSS. The browser can't see it until the CSS is downloaded and parsed, so it starts way too late — LCP at 1.18s.
Without: the image waits for CSS download + parse, then its own connection. With preload: it fetches in parallel from the first byte of HTML.
Get it right

The syntax: as & crossorigin

preload a font and the LCP image
<link rel="preload" href="/hero.webp" as="image" fetchpriority="high">
<link rel="preload" href="/fonts/Inter.woff2" as="font" type="font/woff2" crossorigin>
Two attributes that bite
as tells the browser the resource type (image, font, script…). Get it wrong and the browser can't match the preload to the real request, so it fetches the file twice. crossorigin is required for fonts (and any CORS resource) — omit it and, again, you get a double fetch.
Bundles too

Preloading split bundles

Remember the emoji-picker chunk from bundle splitting? Normally the browser only discovers it needs that chunk after the main script runs. If it's actually critical, you can preload it so it fetches in parallel with the main bundle. With Webpack you don't even hand-write the link — magic comments generate it:

Webpack magic comment → a preload link
import(
  /* webpackPreload: true */
  "./EmojiPicker"
);
// Webpack emits <link rel="preload"> so the chunk loads alongside main.js
The catch

Don't block the parser

preload raises a resource's priority — but for scripts you must make sure the high-priority fetch doesn't turn into the parser waiting on it. The common pattern is to preload and mark the script async/defer, so it downloads early but doesn't stall rendering. And preload is a trade-off: pulling one resource forward can push others back, so reserve it for genuinely critical, late-discovered ones.

Q1Multiple choice
preload is most useful for which kind of resource?
Q2Multiple choice
Why does a wrong as value cause a double fetch?
Q3Multiple choice
What's the difference between preconnect and preload?
Key takeaways
  • preload fetches a critical, late-discovered resource early (fonts, CSS background/LCP images).
  • It can dramatically improve LCP by loading the hero in parallel with the CSS.
  • Set as correctly and add crossorigin for fonts — or you get a double fetch.
  • You can preload split bundles too (Webpack magic comments).
  • For scripts, pair preload with async/defer so it doesn't block the parser.
← Previous
13. Resource Hints — dns-prefetch & preconnect
Next →
15. Resource Hints — prefetch