Best HTML5 Features
How change site speed only with HTML5 feautures
preload — Fetch This Now, It’s Critical
This is the highest-priority hint. Use it for resources your current page definitely needs and needs early.
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.webp" as="image">
This tells the browser: “Don’t wait. Start downloading this immediately.”
- Where I used it:
- Hero image → preloaded
- Main font → preloaded
- Only preload:
- fonts
- critical scripts
- nothing else
prefetch — Fetch This Later, I Think They’ll Need It
If preload is urgent, prefetch is patient.
It says: ”When you have spare bandwidth, quietly go fetch this in the background. The user probably needs it next. When you’re free, grab this. We’ll need it soon.”
<!-- On your /login page, prefetch dashboard assets -->
<link rel="prefetch" href="/dashboard/bundle.js">
<link rel="prefetch" href="/dashboard/main.css">
So when users log in… Dashboard loads almost instantly. Because it’s already cached.
- Where this works best
- Login → Dashboard
- Product → Checkout
- Step 1 → Step 2
- Anywhere the next step is predictable
- The subtle thing most developers miss:
- prefetched resources land in the HTTP cache, not just in memory.
- They survive navigation. When the user actually visits that next page, the browser serves the asset from disk not from a new network request.
- That’s the prefetch trick that turns a 1.2-second navigation into a 100ms one.
preconnect — Warm Up This Domain Before We Need It
This one surprised me the most. Because the delay it fixes is invisible.
Every new domain has setup cost:
- DNS lookup
- TCP connection
- TLS handshake
That can take 100–500ms.
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="preconnect" href="https://api.yourapp.com">
- Before:
- Fonts took time to start loading
- After:
- Connection already ready
- Fonts loaded faster
Here, Google Fonts mistake most devs make. Google Fonts uses two domains:
- fonts.googleapis.com → for CSS
- fonts.gstatic.com → for font files
If you only preconnect one… You still lose time.
The crossorigin attribute on fonts.gstatic.com is required because fonts are fetched with CORS . Leave it off and the preconnect won’t be used for the actual font requests.
How many preconnects is too many? Stick to 2–3 critical origins. Each preconnect keeps an open connection alive, which has memory and CPU cost. Preconnecting to 10 domains helps nobody.
dns-prefetch — Just Look Up the Address (Lightweight Backup)
This is like a softer version of preconnect.
Where preconnect does the full DNS + TCP + TLS handshake, dns-prefetch only resolves the domain name to an IP address. Less powerful, but also less resource-intensive.
<link rel="dns-prefetch" href="https://analytics.yourapp.com">
<link rel="dns-prefetch" href="https://cdn.thirdparty.io">
Use it for domains you’re likely to use but aren’t certain about, or as a fallback for browsers that don’t support preconnect.
-
Modern browsers → use preconnect
-
Older browsers → fall back to DNS
modulepreload — Preload for ES Modules (For Modern JavaScript Apps)
If your app ships native ES modules (no bundler, or partially unbundled), regular preload with as="script" isn’t enough.
modulepreload fetches the module, parses it, and adds it to the module registry. So when your code executes import { thing } from './utils.js', it’s already done.
<link rel="modulepreload" href="/src/utils.js">
<link rel="modulepreload" href="/src/router.js">
<link rel="modulepreload" href="/src/store.js">
- Without this:
- main.js loads
- then utils.js
- then helpers.js
- With modulepreload:
- Everything loads in parallel
- When I use it:
- Vite apps
- Micro frontends
- Unbundled setups
If you’re bundling everything → you can skip this.
A Real That Actually Performs
Here’s what these hints look like working together in a production <head>:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My App</title>
<!-- Step 1: Warm up third-party connections first -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://analytics.myapp.com">
<!-- Step 2: Preload critical current-page assets -->
<link rel="preload" href="/fonts/inter.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.webp" as="image">
<!-- Step 3: Load stylesheets -->
<link rel="stylesheet" href="/styles/main.css">
<link href="https://fonts.googleapis.com/css2?family=Inter&display=swap" rel="stylesheet">
<!-- Step 4: Prefetch likely next pages (background, no urgency) -->
<link rel="prefetch" href="/dashboard/bundle.js">
</head>
- Without instructions:
- He takes one order
- delivers it
- comes back
- repeats
- With instructions:
- “Deliver these first”
- “You’ll need this next”
- “Set up connection here”
That's preload, prefetch, and preconnect in one sentence.
Quick Reference
| rel value | What it does | Use when |
|---|---|---|
| preload | Fetch critical asset immediately | Above-fold images, fonts, key scripts on current page |
| prefetch | Fetch likely-needed asset in background | Assets for the user's probable next page |
| preconnect | Full handshake with a domain early | Critical 3rd-party origins (fonts, APIs, CDNs) |
| dns-prefetch | DNS lookup only | Non-critical origins or as preconnect fallback |
| modulepreload | Fetch + parse ES module early | Native module graphs in unbundled setups |