Topics
v6.0.0
On this page · sections

Resource Hints: Preload, Prefetch, Preconnect

Resource hints tell the browser to resolve DNS, open connections, or fetch resources early — each maps to a different timing in the critical rendering path.

HintPriorityWhen to use
dns-prefetchLowResolve DNS only — cheap warm-up
preconnectHighDNS + TCP + TLS to an origin
preloadHighCurrent page needs this resource now
prefetchLowLikely next navigation
modulepreloadHighES module graph nodes for current page

Preload vs Prefetch vs Preconnect

Definition

rel="preconnect" — opens DNS, TCP, and TLS to an origin early. Use for fonts CDN, API, analytics. Add crossorigin when credentials or CORS matter (fonts require it).

rel="preload" — fetches a resource now at high priority because the current page needs it — hero image, critical font, above-fold CSS. Requires correct as attribute.

rel="prefetch" — fetches at low priority for a future navigation — next route’s JS on link hover or idle. Lands in HTTP cache.

<link rel="preconnect" href="https://api.example.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preload" href="/hero.webp" as="image" type="image/webp" fetchpriority="high"> <link rel="prefetch" href="/_next/static/chunks/dashboard.js" as="script">
document.querySelectorAll('[data-prefetch]').forEach((anchor) => { anchor.addEventListener('mouseenter', () => { const link = document.createElement('link'); link.rel = 'prefetch'; link.href = anchor.dataset.prefetch; link.as = 'script'; document.head.appendChild(link); }, { once: true }); });

Hints are advisory — browsers may ignore under memory pressure or Save-Data.

Interview: preconnect = origin warm-up. preload = this page, high priority. prefetch = next page, low priority.

modulepreload

Definition

ES modules form a dependency graph. modulepreload fetches module scripts early for instant evaluation — plain preload as="script" targets classic scripts without module semantics.

<link rel="modulepreload" href="/assets/vendor-react.js"> <link rel="modulepreload" href="/assets/page-home.js"> <script type="module" src="/assets/page-home.js"></script>

List direct dependencies of the entry — the browser doesn’t auto-preload the entire transitive graph from one tag. Vite and Next emit modulepreload in production HTML for entry and shared chunks.

Cross-origin modulepreload needs matching crossorigin attribute. Duplicate preload + modulepreload for the same URL wastes bandwidth.

Interview: modulepreload for ES module entries and shared vendor chunks — not lazy routes the user may never visit.

Fetch Priority and as Attribute

Definition

Wrong as type creates two cache keys — browser downloads twice.

<!-- WRONG — two downloads --> <link rel="preload" href="/fonts/inter.woff2" as="fetch"> <!-- CORRECT --> <link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>

fetchpriority="high" on LCP image; fetchpriority="low" on below-fold. Responsive images:

function responsivePreload(srcset, sizes) { const link = document.createElement('link'); link.rel = 'preload'; link.as = 'image'; link.imagesrcset = srcset; link.imagesizes = sizes; link.fetchPriority = 'high'; document.head.appendChild(link); } if (!navigator.connection?.saveData) { // prefetch only when user hasn't enabled Save-Data }

Mis-prioritized preload saturates bandwidth and delays LCP. Preloading every route bundle on landing page is an anti-pattern.

Interview: Match as to eventual request type. Preload competes with render-critical resources.

Next.js and Framework Patterns

Definition

Next.js App Router defaults:

  • Fonts — next/font inlines or self-hosts; avoids runtime Google Fonts preconnect dance
  • Images — next/image generates srcset; use priority prop for LCP image (emits preload)
  • Routes — <Link prefetch> prefetches linked routes in viewport (production)
import Image from 'next/image'; import Link from 'next/link'; export default function Page() { return ( <> <Image src="/hero.webp" alt="Hero" width={1200} height={630} priority // LCP — adds preload + fetchpriority high /> <Link href="/dashboard" prefetch> Dashboard </Link> </> ); }

Manual hints in layout.tsx or middleware Link headers for API origins. Don’t duplicate what the framework already emits — inspect production HTML.

Interview: Use framework primitives (priority, prefetch) before hand-rolling link tags.

Multi-Route SaaS Strategy

Definition

Staff-level hint policy per route archetype:

RoutePreconnectPreloadPrefetch
LandingAPI, fonts CDNHero, critical CSSDashboard bundle on CTA hover
App shellAPIShell CSS, vendor modulepreloadAdjacent nav routes
AuthIdentity provider—Post-login dashboard

Governance rules:

  1. Budget — max 2 preconnects, 3 preloads per route
  2. RUM validation — measure LCP subparts before/after hints
  3. Save-Data — skip prefetch when navigator.connection.saveData
  4. CDN Vary — preloaded assets must match cache keys
  5. CI check — lint for duplicate or wrong as types

Audit quarterly — hints drift as routes change. Early hints (103 response) let browser fetch during server think time.

Interview: Hints are a budget, not a checklist. Preload only what the current page needs for LCP.

Senior Interview Summary

Mental model

dns-prefetch → DNS only preconnect → DNS + TCP + TLS preload → Fetch now (this page) prefetch → Fetch later (next page) modulepreload → ES module early fetch

Practical rules

  • preconnect for origins hit on first paint
  • preload LCP image/font with correct as + crossorigin
  • prefetch on hover for likely next route — not all routes at boot
  • Respect Save-Data and bandwidth limits
  • Validate with LCP breakdown, not Lighthouse alone

What to say in interviews

“For a landing page I’d preconnect to the API and font CDN, preload the hero image with as=image and fetchpriority high, and prefetch the dashboard chunk on CTA hover. I’d avoid preloading every route — it steals LCP bandwidth. modulepreload for the entry and shared vendor chunks in a Vite/Next app.”

Common traps

  • Wrong as → double download
  • Preloading next-route JS on LCP-critical pages
  • Missing crossorigin on font preload
  • Duplicating framework-generated hints

Last updated on