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.
| Hint | Priority | When to use |
|---|---|---|
dns-prefetch | Low | Resolve DNS only — cheap warm-up |
preconnect | High | DNS + TCP + TLS to an origin |
preload | High | Current page needs this resource now |
prefetch | Low | Likely next navigation |
modulepreload | High | ES 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
asto eventual request type. Preload competes with render-critical resources.
Next.js and Framework Patterns
Definition
Next.js App Router defaults:
- Fonts —
next/fontinlines or self-hosts; avoids runtime Google Fonts preconnect dance - Images —
next/imagegenerates srcset; usepriorityprop 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:
| Route | Preconnect | Preload | Prefetch |
|---|---|---|---|
| Landing | API, fonts CDN | Hero, critical CSS | Dashboard bundle on CTA hover |
| App shell | API | Shell CSS, vendor modulepreload | Adjacent nav routes |
| Auth | Identity provider | — | Post-login dashboard |
Governance rules:
- Budget — max 2 preconnects, 3 preloads per route
- RUM validation — measure LCP subparts before/after hints
- Save-Data — skip prefetch when
navigator.connection.saveData - CDN
Vary— preloaded assets must match cache keys - CI check — lint for duplicate or wrong
astypes
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 fetchPractical 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
crossoriginon font preload - Duplicating framework-generated hints