HTML document and meta · 5 / 8
lesson 5

Resource hints

preload, prefetch, preconnect, dns-prefetch, modulepreload — five hints, each useful in a different moment.

~ 17 min read·lesson 5 of 8
0 / 8

Every resource your page loads costs a few specific things: a DNS lookup (find the host), a TCP+TLS handshake (open the connection), and the fetch itself (download the bytes). For a critical resource — the hero image, your font, the JavaScript that runs first — those steps happen serially. The user waits.

Resource hints let you tell the browser to start some of those steps early, in parallel with the rest of the page loading. The right hint on the right resource shaves hundreds of milliseconds off the time-to-interactive. The wrong hint wastes bandwidth.

This lesson covers the five hints that earn their place.

preconnect and dns-prefetch

The cheapest hint. preconnect tells the browser to do a DNS lookup, TCP handshake, and TLS negotiation for a host now, before any resource on that host is needed.

preconnect.html
<link rel="preconnect" href="https://images.example.com">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

By the time your page needs a font from fonts.gstatic.com, the connection is already open. The actual fetch starts immediately instead of waiting on the handshake.

The crossorigin attribute is required when the resource will be fetched without credentials (typical for fonts). Without it, the preconnect opens a different connection than the eventual fetch, wasting the work.

dns-prefetch is the cheaper, weaker version — DNS lookup only, no TCP/TLS handshake.

dns-prefetch.html
<link rel="dns-prefetch" href="https://images.example.com">

Use dns-prefetch when there are many hosts you might use but you do not want the cost of opening connections to all of them. Use preconnect for the 2-3 hosts you definitely will use. Most modern sites use preconnect exclusively; dns-prefetch is a fallback for older browsers.

Watch out

Don't preconnect to every host you use. Each preconnect costs a real TCP/TLS handshake — the browser is doing work the moment it sees the link. More than 4-5 preconnects becomes counterproductive. Pick the ones you need first.

preload

preload tells the browser to fetch a specific resource right now — not just the connection, the actual file. Use it for resources you know the page will need, but which the browser would otherwise discover late.

preload-font.html
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>

The browser fetches inter.woff2 immediately, in parallel with the rest of the head. By the time CSS triggers the font, the file is in cache.

The as attribute is mandatory and tells the browser what kind of resource this is — font, style, script, image, video, audio, fetch. Get it wrong and the browser may not actually use the preloaded resource (it sees the type it expected does not match).

The type attribute (font/woff2, image/avif) helps the browser skip a preload for resources it cannot decode.

The crossorigin attribute is required for fonts and any cross-origin fetch.

A common preload pattern: the LCP hero image.

preload-image.html
<link rel="preload" as="image" href="/hero.avif" type="image/avif" fetchpriority="high">

The hero image starts downloading the moment the browser parses the head, not when it first encounters the <img> tag halfway down the body. For LCP-critical images, this is one of the biggest wins available.

Watch out

Preload only what the page definitely needs in the next moments. A preloaded resource that goes unused is pure waste — the browser fetched it for nothing. The Network panel shows a "preload but not used" warning if you go wrong.

check your understanding
Your hero image is the LCP element. Without changes, the browser only discovers it once it parses the body and finds the <img>. Which hint helps the most?

modulepreload

modulepreload is preload for ES modules. It fetches the module and parses it, so when an import statement runs, the module is already ready to execute.

modulepreload.html
<link rel="modulepreload" href="/app.js">
<link rel="modulepreload" href="/util.js">

For a page using ES modules with a complex dependency graph, modulepreloading the entry and key dependencies cuts the import waterfall significantly. The browser can fetch and parse them in parallel instead of one after another.

For traditional non-module scripts, <link rel="preload" as="script"> is the equivalent. Pick modulepreload when your scripts use ES module syntax (import/export); pick preload as="script" for everything else.

prefetch

prefetch is the opposite of preload. It tells the browser to fetch a resource at low priority — when there is idle time — for a future navigation.

prefetch.html
<link rel="prefetch" href="/articles/sourdough-starter">

The most common use: prefetch the next page the user is likely to visit. On a "Read next" suggestion, prefetch the article so when the user clicks, it loads instantly from cache.

Many frameworks (Next.js, Astro, Remix) prefetch links in the viewport automatically. You rarely need to write prefetch by hand if you use one.

The cost is bandwidth — prefetching a page the user does not visit wasted their data. So prefetch only the next-most-likely action, not every link on the page.

prefetch is for future navigations. preload is for the current page. Different verbs, different jobs.

When to use which

A short cheat sheet.

  • preconnect — for hosts you will fetch from soon (your CDN, your fonts host). Cheap, high-leverage, two or three per page.
  • dns-prefetch — for hosts you might fetch from. Cheaper than preconnect; use when you cannot commit to a real connection.
  • preload — for specific resources you know the page needs immediately (the LCP image, the critical font, a critical CSS chunk).
  • modulepreload — like preload but for ES modules; fetches and parses ahead of the import.
  • prefetch — for future navigations; the next page the user is likely to click.

A typical head with hints:

head-with-hints.html
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sourdough season</title>
<meta name="description" content="...">

<!-- Connections we'll use -->
<link rel="preconnect" href="https://images.example.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- The LCP image specifically -->
<link rel="preload" as="image" href="/hero.avif" type="image/avif" fetchpriority="high">

<!-- Critical font -->
<link rel="preload" as="font" type="font/woff2" href="/fonts/inter.woff2" crossorigin>

<!-- The CSS itself -->
<link rel="stylesheet" href="/styles.css">
<script defer src="/app.js"></script>
</head>

Two preconnects, two preloads, one stylesheet, one deferred script. Each line earning its place.

A useful picture: resource hints are like telling a runner which cones to set up before the race. The right cones (preconnect, preload) shave seconds off the run. Setting up a cone you do not run past is wasted work.

check your understanding
You add <link rel="preload" as="font" href="/fonts/inter.woff2"> with no crossorigin attribute. What's likely to happen?
check your understanding
You preload a font, but the Network panel shows it as "preloaded but not used within ~3 seconds". What does that warning suggest?
check your understanding
Your blog page has a "Read next" link to another article. You want the next article to load instantly when the user clicks. Which hint is right?
← prevnext lesson →
KeepLearningcertificate
for completing
HTML document and meta
0 of 8 read