Skip to content

ADR-028: Prefetch Strategy — @astrojs/prefetch

Superseded — @astrojs/prefetch was deprecated in Astro 3.5 and removed in Astro 4.0. The project now uses Astro’s built-in prefetch: true config option in astro.config.mjs, which provides equivalent hover/focus-based prefetching natively. See Astro Prefetch Guide for the current approach.

The starter includes @astrojs/prefetch in astro.config.mjs. This integration adds a small JavaScript snippet (~1KB) that prefetches pages when a user hovers over or focuses an internal link, making subsequent navigations feel instant.

This is an undocumented addition that adds JavaScript to every page — a tension with the zero-JS philosophy. This ADR justifies the inclusion and defines the configuration defaults.

  • Perceived performance: Prefetching eliminates the network round-trip on navigation, making the site feel faster even when Lighthouse scores are already high
  • JS budget: The addition must be minimal — under 2KB gzipped
  • Zero-JS philosophy: Any JavaScript addition requires explicit justification (per ADR-001 pattern)
  • Progressive enhancement: Prefetching is purely additive — pages load correctly without it
  • Works with View Transitions: Prefetch + ClientRouter (ADR-009) together produce near-instant navigation

Pros:

  • Strictly zero JavaScript for navigation
  • No speculative network requests

Cons:

  • Full network round-trip on every navigation
  • Noticeably slower perceived performance on multi-page sites
  • Wastes the benefit of static hosting (files are already on CDN edge)

Option 2: @astrojs/prefetch with intentSelector (hover/focus) — chosen

Section titled “Option 2: @astrojs/prefetch with intentSelector (hover/focus) — chosen”

Pros:

  • Prefetch only triggers on user intent (hover/focus), not on page load
  • No wasted bandwidth for links the user never clicks
  • ~1KB gzipped — negligible JS budget impact
  • Works automatically with all <a> tags pointing to internal routes

Cons:

  • Adds JavaScript to every page
  • Hover-based prefetch doesn’t help on touch-only devices (touch triggers click, not hover)
Section titled “Option 3: <link rel="prefetch"> in <head> (manual)”

Pros:

  • No JavaScript at all — pure HTML hint
  • Browser decides whether to act on it

Cons:

  • Must be manually added per page
  • No dynamic intent detection
  • Prefetches unconditionally on page load (wastes bandwidth)

Pros:

  • Native browser API, no JavaScript library
  • Supports prerendering (not just prefetching)

Cons:

  • Chrome-only as of 2026 — not cross-browser
  • Prerendering has privacy implications (executes page JS before user navigates)
  • Not yet suitable as a default

Keep @astrojs/prefetch with default intentSelector behaviour (hover + focus triggers prefetch).

The ~1KB cost is justified by the significant perceived performance improvement on multi-page navigation. This is consistent with the ADR-009 decision to include ClientRouter (~2-3KB) for the same reason: small, justified JS additions that improve perceived performance are acceptable when they degrade gracefully.

astro.config.mjs
import prefetch from '@astrojs/prefetch';
export default defineConfig({
integrations: [
prefetch(), // defaults: intentSelector triggers on hover/focus
],
});

No custom configuration is needed. The default behaviour is correct for this use case.

On touch devices, hover events don’t fire before click. Prefetch therefore provides no benefit on touch-only interactions. This is acceptable — the site loads correctly without prefetch, and touch users on fast connections won’t notice the difference. Touch users on slow connections benefit from the static CDN delivery regardless.

  • Near-instant navigation when combined with ClientRouter (ADR-009)
  • Zero configuration required
  • Gracefully degrades — pages work without it
  • Adds ~1KB JavaScript to every page
  • No benefit on touch-only devices
  • Speculative network requests may be unwanted on metered connections (mitigated by intent-based triggering)
  • Total JavaScript from prefetch + ClientRouter: ~4KB gzipped — well within the 160KB budget
  • Speculation Rules API should be re-evaluated when cross-browser support improves
  • Bundle size: @astrojs/prefetch contribution must remain under 2KB gzipped
  • Lighthouse: No regression in Performance score from prefetch script
  • Network tab: Prefetch requests only fire on hover/focus, not on page load

Not enforced — this record’s status is Superseded; only Accepted ADRs are binding (see the status table in the ADR README and ADR-039).


Date: 2026-02-18
Participants: Template maintainers
Outcome: Superseded (replaced by built-in prefetch: true config)