Difficulty: Advanced
Resource hints are HTML mechanisms that allow developers to inform the browser about resources it will need in the near future, enabling the browser to start fetching or preparing connections before they are actually required. The four primary resource hints are preload, prefetch, preconnect, and dns-prefetch. Each serves a different purpose and operates at a different priority level, and using them correctly can significantly improve perceived and actual page load performance.
Preload (link rel='preload') tells the browser to fetch a specific resource immediately with high priority because it is needed for the current page. Unlike normal resource discovery, which happens as the browser parses HTML and encounters references, preload forces the browser to start downloading a resource before it would naturally discover it. This is critical for resources referenced in CSS (like fonts and background images) or loaded via JavaScript, which the browser cannot discover until it has parsed and executed those files. Preload requires the 'as' attribute to specify the resource type (font, image, script, style, fetch) so the browser can apply correct caching headers and Content Security Policy checks.
Prefetch (link rel='prefetch') is a low-priority hint that tells the browser to fetch a resource that will likely be needed for a future navigation. The browser downloads prefetched resources during idle time, so they do not compete with current page resources. This is ideal for pre-loading the next page's JavaScript bundle, CSS, or data when you can predict where the user will navigate next. Prefetch is purely speculative and browsers may ignore it if the device is on a slow connection, has limited memory, or data saver mode is enabled.
Preconnect (link rel='preconnect') instructs the browser to establish an early connection to a specified origin, completing the DNS lookup, TCP handshake, and TLS negotiation before any request is made. This eliminates the connection setup latency (often 100-500ms) when the actual request occurs. Preconnect is most valuable for third-party origins like CDNs, font services, analytics endpoints, and API servers. Use it sparingly -- each preconnect maintains an open connection that consumes resources, so limit it to 2-4 critical origins.
DNS-prefetch (link rel='dns-prefetch') is a lighter version of preconnect that only performs the DNS lookup step. It resolves a domain name to an IP address, which typically takes 20-120ms. DNS-prefetch has wider browser support than preconnect and is appropriate for origins where you want to reduce latency but the connection is not critical enough to warrant a full preconnect. It is common to use both as a progressive enhancement: set preconnect for modern browsers and dns-prefetch as a fallback for older ones.
The fetchpriority attribute (available on img, script, link, and iframe elements) complements resource hints by letting you adjust the priority of individual resources. Setting fetchpriority='high' on the LCP image ensures it loads before other images. Setting fetchpriority='low' on non-critical scripts prevents them from competing with important resources. Combining fetchpriority with preload gives you precise control over what loads first and at what priority, which is essential for optimizing Core Web Vitals.
<head>
<!-- Preload a font file (must include crossorigin for fonts) -->
<link
rel="preload"
href="/fonts/Inter-Regular.woff2"
as="font"
type="font/woff2"
crossorigin="anonymous"
/>
<!-- Preload the hero image for faster LCP -->
<link
rel="preload"
href="/images/hero-banner.webp"
as="image"
type="image/webp"
/>
<!-- Preload a critical CSS file -->
<link
rel="preload"
href="/css/critical.css"
as="style"
/>
<link rel="stylesheet" href="/css/critical.css" />
<!-- Preload a JavaScript module -->
<link
rel="modulepreload"
href="/js/app.mjs"
/>
</head>
Preload forces the browser to start downloading critical resources immediately. Fonts require the crossorigin attribute even for same-origin requests. The 'as' attribute is mandatory and must match the resource type for correct prioritization. Use modulepreload for ES modules instead of preload with as='script'.
<head>
<!-- Prefetch the next page's JavaScript bundle -->
<link rel="prefetch" href="/js/dashboard.chunk.js" />
<!-- Prefetch the next page's CSS -->
<link rel="prefetch" href="/css/dashboard.css" />
<!-- Prefetch a JSON data file -->
<link rel="prefetch" href="/api/user/dashboard-data" />
</head>
<!-- Dynamic prefetch on hover (common SPA pattern) -->
<script>
document.querySelectorAll('a[data-prefetch]').forEach((link) => {
link.addEventListener('mouseenter', () => {
const href = link.getAttribute('href');
const prefetchLink = document.createElement('link');
prefetchLink.rel = 'prefetch';
prefetchLink.href = href;
document.head.appendChild(prefetchLink);
}, { once: true });
});
</script>
<nav>
<a href="/dashboard" data-prefetch>Dashboard</a>
<a href="/settings" data-prefetch>Settings</a>
</nav>
Prefetch loads resources with low priority during idle time. The dynamic prefetch pattern creates link elements on hover, giving the browser a head start on loading the next page's resources. The { once: true } option ensures each link is only prefetched once.
<head>
<!-- Preconnect to Google Fonts (DNS + TCP + TLS) -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossorigin="anonymous"
/>
<!-- Preconnect to your API server -->
<link rel="preconnect" href="https://api.example.com" />
<!-- DNS-prefetch as fallback for older browsers -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com" />
<link rel="dns-prefetch" href="https://fonts.gstatic.com" />
<!-- DNS-prefetch for analytics (not critical enough for preconnect) -->
<link rel="dns-prefetch" href="https://analytics.example.com" />
<link rel="dns-prefetch" href="https://cdn.example.com" />
</head>
Preconnect eliminates connection setup latency for critical third-party origins. Google Fonts requires preconnect to two origins: googleapis.com serves the CSS, gstatic.com serves the font files. DNS-prefetch is used as a fallback and for less critical origins where a full preconnect would waste resources.
<!-- High priority for LCP image -->
<img
src="hero.webp"
alt="Hero banner"
width="1200"
height="600"
fetchpriority="high"
/>
<!-- Low priority for below-the-fold images -->
<img
src="footer-decoration.webp"
alt="Decorative footer"
width="200"
height="100"
fetchpriority="low"
loading="lazy"
/>
<!-- High priority for critical script -->
<script src="/js/critical-init.js" fetchpriority="high"></script>
<!-- Low priority for non-critical script -->
<script src="/js/analytics.js" fetchpriority="low" defer></script>
<!-- Preload with high priority -->
<link
rel="preload"
href="/images/lcp-image.webp"
as="image"
fetchpriority="high"
/>
The fetchpriority attribute overrides the browser's default resource priority. Set 'high' on LCP images and critical scripts, 'low' on non-essential resources. This works on img, script, link, and iframe elements. Combining fetchpriority='high' with preload ensures the LCP image loads as fast as possible.
link rel=preload, link rel=prefetch, link rel=preconnect, link rel=dns-prefetch, Resource Hints, Priority Hints