Resource Hints and Navigation Speed
A static page's HTML arrives fast — it is a file on a CDN edge. What happens next decides how fast the page feels: the browser discovers the stylesheet, then the font the stylesheet references, then the hero image the CSS lays out, each one a round trip after the last. And when the reader clicks a link, the whole sequence starts again from nothing. Resource hints and navigation APIs attack both problems: they let you tell the browser what it will need before it discovers it, and they let the next page start loading before the click.
This topic covers the hints that matter for static sites — preconnect, dns-prefetch, preload, modulepreload, prefetch and fetchpriority — the Speculation Rules API for prerendering likely next pages, and cross-document view transitions that make multi-page navigation feel like an app. It sits inside Performance Optimization & Core Web Vitals for SSGs, next to the LCP work in Largest Contentful Paint Optimization for Static Sites.
Why Static Sites Benefit More Than Most
Three properties make hints unusually effective on static sites. First, the HTML is known at build time, so the generator can compute exactly which font, image and stylesheet each page needs and emit precise hints — no guessing at runtime. Second, pages have no server-side side effects, so prerendering a page the reader never visits costs only bandwidth; nothing is logged, charged or mutated. Third, most static sites are multi-page apps, where every navigation is a full document load, so the gap between "click" and "painted" is the largest cost readers pay repeatedly — and the one speculation rules remove.
The flip side is that static generators make it easy to emit hints everywhere. A layout that preloads four fonts, two stylesheets and the logo on every page turns hints into noise that competes with the one resource each page actually needs first. The discipline is to hint little and precisely.
Connection Hints: preconnect and dns-prefetch
A request to a new origin needs DNS resolution, a TCP connection and a TLS handshake before the first byte — typically 100–300 ms on mobile networks, more for distant servers. preconnect does that work early for an origin you know the page will use; dns-prefetch does only the DNS step and is cheaper.
<link rel="preconnect" href="https://images.example-cdn.com" crossorigin>
<link rel="dns-prefetch" href="https://plausible.io">
On a static site that self-hosts fonts and scripts, most critical resources are same-origin and need no connection hint at all. The typical legitimate use is an image CDN serving the LCP image, which should get a preconnect; analytics and other late, non-critical third parties get at most a dns-prefetch. Measured on a blog with an external image CDN, adding preconnect cut LCP by 140 ms on simulated 4G; adding preconnects for three more origins used later in the page added 30 ms back, because the extra handshakes competed for the radio. Preconnect vs DNS-Prefetch on Static Sites has the full measurements.
Priority Hints: preload, modulepreload and fetchpriority
preload tells the browser to fetch a resource immediately at high priority, before the parser would discover it. It is valuable for exactly one class of resource: something critical that is discovered late. On static sites that usually means a web font referenced from CSS, a background-image hero set in CSS, or the entry module of a JavaScript island.
<link rel="preload" href="/fonts/inter-var-latin.woff2" as="font" type="font/woff2" crossorigin>
<link rel="modulepreload" href="/_astro/Search.a1b2c3.js">
<img src="/img/hero-1200.avif" fetchpriority="high" width="1200" height="630" alt="…">
fetchpriority="high" on the LCP <img> is often better than a preload: the image is already in the HTML, so it is discovered early, but browsers initially load images at low priority until layout confirms they are in the viewport. Raising the priority removed 300–500 ms of that delay on the hero templates measured in Optimizing LCP on Astro with Priority Hints.
The failure mode is excess. Every preload competes with every other high-priority request. A page that preloads six resources effectively preloads none, and preloads for resources the page never uses waste bandwidth outright. Auditing Unused Preloads shows how to find and remove them across a whole site.
Next-Page Hints: prefetch and Speculation Rules
For navigation, <link rel="prefetch"> downloads a URL at idle priority into the HTTP cache so the next page's HTML (and optionally its assets) is local when the reader clicks. It is widely supported, cheap, and a good default for "the obvious next page" — the next article in a series, the first result of a list.
The Speculation Rules API goes further: it can prerender the next page entirely — fetch it, run its scripts, lay it out and paint it in a hidden tab — so that activation on click is near-instant. On static pages with no side effects it is the single most dramatic navigation improvement available.
<script type="speculationrules">
{
"prerender": [{
"where": { "and": [
{ "href_matches": "/*" },
{ "not": { "selector_matches": "[rel~=nofollow], [data-no-prerender]" } }
]},
"eagerness": "moderate"
}]
}
</script>
"eagerness": "moderate" prerenders a link after the pointer rests on it for about 200 ms (or on pointer-down on touch devices). On a docs site this made the median navigation — click to LCP of the next page — fall from 620 ms to 40 ms in Chrome, measured with RUM over two weeks. Browsers without support ignore the script. The details, including memory limits and analytics handling, are in Instant Navigation with Speculation Rules; Astro's built-in equivalent is covered in Prefetching Links in Astro.
Cross-Document View Transitions
Single-page apps animate between routes; multi-page static sites traditionally flash white. Cross-document view transitions close that gap with CSS alone: opt in on both pages, and the browser snapshots the old page, loads the new one, and animates between them.
@view-transition { navigation: auto; }
.site-header { view-transition-name: header; }
@media (prefers-reduced-motion: reduce) {
@view-transition { navigation: none; }
}
Combined with speculation rules, a prerendered page plus a 200 ms cross-fade makes a static site feel like an app without shipping a router. The transition starts after the new page's first frame is ready, so it does not delay LCP. View Transitions on Multi-Page Static Sites covers naming elements, avoiding layout shift during the animation and measuring its effect.
The Back/Forward Cache: Free Instant Navigations
The fastest navigation of all is one the browser does not have to perform. When a reader presses Back, a page stored in the back/forward cache (bfcache) is restored from memory in its exact previous state — scroll position, form input, running timers paused — with an LCP near zero. On content sites, back navigations are a large share of all navigations: 18% on the docs site measured here, higher on blogs where readers bounce between an index and articles.
Static sites are eligible by default, but a few common additions quietly disqualify every page. An unload event listener, often added by an old analytics snippet or a consent manager, blocks bfcache in most browsers. A Cache-Control: no-store header on HTML blocks it in Chrome. Open BroadcastChannel or WebSocket connections can block it too. Chrome DevTools' Application panel has a "Back/forward cache" test that navigates away and back and lists every blocking reason for the current page.
On the docs site, removing one unload handler from a legacy analytics script raised the share of back navigations restored from bfcache from 3% to 89%. Field LCP for back navigations fell from 540 ms to 30 ms, and because back navigations were nearly a fifth of all navigations, the site-wide p75 LCP improved by about 90 ms with no other change. Replace unload with pagehide or visibilitychange, and check the DevTools panel after adding any third-party script.
Hints, Caching and Repeat Visits
Resource hints and HTTP caching interact in ways that are easy to get wrong. A preload for a font that is already in the HTTP cache costs almost nothing — the browser satisfies it locally — so preloads are cheap on repeat views as long as the asset URL is stable. That is one more reason to fingerprint asset filenames and serve them with long immutable cache lifetimes, as described in Cache Busting with Content-Hashed Filenames.
Prefetched and prerendered pages are more fragile. A prefetched HTML document is only reused if the eventual navigation matches the cached response; if the HTML is served with Cache-Control: no-cache or a short max-age and the prefetch happened long enough ago, the browser may revalidate or refetch. Speculation rules sidestep much of this because the prerendered page is held in memory rather than in the HTTP cache, but they are discarded after a few minutes if unused, and the browser limits how many can exist at once. For static HTML, a short max-age of a few minutes with stale-while-revalidate keeps prefetches reusable without delaying content updates noticeably; the trade-offs are in Stale-While-Revalidate for Static HTML.
Respecting Constrained Devices and Data Saver
Next-page hints spend the reader's bandwidth on a guess. On unmetered broadband that is a good trade; on a metered mobile plan or a slow connection it may not be. Browsers already guard some of this — Chrome disables speculation rules when Data Saver or battery saver is on, and when memory is low — but explicit prefetch code you write yourself does not get those guards automatically.
If you add custom prefetching, check navigator.connection?.saveData and skip prefetches when it is true, and skip them on effectiveType values of 2g or slow-2g. Keep eagerness moderate rather than eager, so that pages are prerendered on signals of intent rather than on page load. And exclude large downloads — PDFs, archives, video pages — from speculation rules with a not clause, because prerendering a 40 MB download page on hover is a real cost with little benefit. These guards cost a few lines and make the feature defensible to readers who pay for data.
Emitting Hints From Your Generator
Hints should be computed per page at build time, not hard-coded in a layout. Each generator has a natural place for that:
- Astro emits
modulepreloadfor island entry points automatically, and its<Image>component acceptsfetchpriorityandloading; prefetching is a built-in option (prefetch: truein the config). - Eleventy can emit a page-specific preload from front matter (
preloadImage: /img/hero.avif) in the base layout, keeping hints next to the content that needs them. - Hugo can compute the hero image with
resources.Getand.Resize, then emit<link rel="preload" as="image" imagesrcset="…">with the exact responsive set in the page's head partial. - Next.js static export emits preloads for route chunks; the
next/imagepriorityprop addsfetchpriority="high"and a preload for the LCP image.
The goal is the same everywhere: one precise hint for the page's LCP resource, one for its critical font if it is discovered late, and nothing else in the head by default.
A Hint Budget per Template
Because hints compete, budget them like bytes. A reasonable starting budget for a content template: at most one preload for a font, at most one high-priority image, at most two preconnects, any number of dns-prefetches for non-critical third parties, and speculation rules with moderate eagerness. Assert it in CI by parsing the built HTML: count rel="preload" and rel="preconnect" per template and fail when a template exceeds its allowance. On one Hugo site this check caught a theme update that added preloads for all six font weights to every page — which had raised LCP by 280 ms on the text-heavy templates.
Measuring Whether Hints Help
Hints change timing, so verify each one with numbers rather than intuition. For current-page hints, compare lab LCP with and without the hint using the median of five Lighthouse runs, and confirm in the waterfall that the hinted resource actually started earlier. For next-page hints, lab tools do not help: measure in the field. The web-vitals library reports navigationType (navigate, prerender, back-forward and so on), so RUM data can compare LCP for prerendered navigations against normal ones directly. On the docs site above, 71% of internal navigations in Chrome were prerendered after enabling speculation rules, and those had a median LCP of 40 ms against 610 ms for the rest.
Measured Impact
A 900-page documentation site on Eleventy applied the approach in this topic over one month: removed four unnecessary preloads per page, kept one font preload and fetchpriority on hero images, added speculation rules with moderate eagerness, fixed a bfcache blocker, and enabled cross-document view transitions. Field numbers are p75 from RUM, mobile and desktop combined, over comparable four-week windows.
| Measure | Before | After |
|---|---|---|
| Preload and preconnect tags per page (median) | 7 | 2 |
| LCP p75, first navigation | 1.9 s | 1.6 s |
| LCP p75, internal navigations | 1.1 s | 0.3 s |
| Back navigations restored from bfcache | 3% | 89% |
| Pages per session | 3.4 | 3.9 |
The pages-per-session change is the one to be careful interpreting — it coincided with other content work — but readers clearly were not deterred by faster navigation.
Common Pitfalls
- Preloading everything. More than two or three preloads per page and they cancel each other out. Hint only late-discovered critical resources.
- Forgetting
crossoriginon font preloads. Fonts load in CORS mode; without the attribute the preload is not reused and the font downloads twice. - Preconnecting to origins used late. Connections close after about ten seconds idle. Preconnect only to origins needed in the first moments of the load.
- Prerendering with side effects. Analytics that fire on script execution count prerendered pages the reader never sees. Defer them until
prerenderingchange. - Eager speculation on huge link lists.
eagerness: "eager"on a page with 200 links prerenders far too much. Usemoderateand exclude large downloads. - Hard-coded hints in layouts. They drift out of date when assets change. Generate them per page at build time.
Key Takeaways
- Current-page hints shorten the critical path; next-page hints remove navigation latency. Use both, sparingly.
fetchpriority="high"on the LCP image and one font preload deliver most of the current-page gain.- Speculation rules with moderate eagerness cut click-to-LCP from 620 ms to 40 ms on a static docs site in Chrome.
- Cross-document view transitions add app-like navigation to multi-page sites with a few lines of CSS.
- Budget hints per template and verify each with the waterfall and field data.
FAQ
What is the difference between preload and prefetch?
Preload fetches a resource the current page needs soon, at high priority. Prefetch fetches something a future navigation will probably need, at the lowest priority, when the browser is idle. Using preload for future pages wastes bandwidth the current page needs.
Are speculation rules safe on a static site?
Yes, and static sites are the ideal case. Prerendering a static page has no server-side side effects, so the main costs are bandwidth and memory. Use moderate eagerness so pages are prerendered on hover or pointer-down rather than on load.
How many preconnect hints should a page have?
Two or three at most, and only for origins used early in the page load. Each preconnect opens a connection that costs CPU and may be closed unused after about ten seconds.
Do view transitions slow pages down?
Not measurably when used for simple cross-document transitions. The transition runs after the new page has rendered its first frame, so it does not delay LCP. Keep animations short and respect prefers-reduced-motion.
Can resource hints hurt performance?
Yes. Preloading resources that are not used, or preloading too many, competes with critical resources for bandwidth. Lighthouse flags unused preloads, and field LCP often improves when excess hints are removed.
Related
- Up: Performance Optimization & Core Web Vitals for SSGs — where hints fit among the other levers.
- Preconnect vs DNS-Prefetch on Static Sites — connection hints, measured.
- Instant Navigation with Speculation Rules — prerendering the next page.
- Prefetching Links in Astro — the framework-native option.
- View Transitions on Multi-Page Static Sites — animating navigations without a router.
- Auditing Unused Preloads — removing hints that hurt.
- Preloading Fonts Without Double Downloads — the font preload done correctly.
- Largest Contentful Paint Optimization for Static Sites — the metric most hints serve.