Preloading reduces perceived latency by ensuring images are fetched and cached before they are needed by the UI. Use preloading for hero images, gallery thumbnails, or icons that must appear instantly when a view opens.
Quick comparison
| Method | Complexity | Best for | Pros | Cons |
|---|---|---|---|---|
| Image object | Low | Simple prefetch of a few images | Works in all browsers; easy to implement | Manual cache control; no priority hint |
| <link rel=preload> | Low–Medium | Critical images (hero, above-the-fold) | Browser-level priority hint; efficient | Must set correct attributes; careful with duplicates |
| Fetch + blob | Medium | When you need programmatic control or transforms | Full control over response; can convert to object URL | More code; memory management for blobs |
| CSS background preload | Low | Background images used by CSS | Simple; integrates with stylesheets | Less explicit control; depends on CSS loading order |
| Framework APIs / hooks | Low–Medium | React / frameworks with SSR or resource hints | Integrates with rendering lifecycle; server hints possible | Framework-specific; requires correct usage |
Detailed options with TypeScript examples
1. Create an Image object (simplest)
Instantiates `HTMLImageElement` and sets `src`. The browser fetches and caches the image so later `` tags load instantly. This is the most common and portable approach.
// TypeScript: preload a list of URLs
function preloadImages(urls: string[]): Promise {
return Promise.all(urls.map(url => new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve();
img.onerror = () => reject(new Error(`Failed to load ${url}`));
img.src = url;
})));
}
// Usage
preloadImages(['/img/hero.jpg', '/img/thumb1.jpg']).catch(console.error);
2. <link rel="preload" as="image"> (browser hint)
Tells the browser to fetch a resource with higher priority and treat it as an image. Use for critical images that must be available immediately. This is a browser-level hint and can be added in HTML or injected dynamically.
<!-- In your HTML head for a critical hero image -->
<link rel="preload" as="image" href="/img/hero.jpg" imagesrcset="/img/hero-1x.jpg 1x, /img/hero-2x.jpg 2x" imagesizes="100vw">
<!-- Or inject from TypeScript -->
function injectPreload(href: string) {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'image';
link.href = href;
document.head.appendChild(link);
}
3. Fetch API → blob → object URL (programmatic control)
Downloads the image via `fetch`, optionally processes it, then creates an object URL for use in the DOM. Use when you need to transform images or control caching behavior.
async function preloadAsBlob(url: string): Promise {
const res = await fetch(url);
if (!res.ok) throw new Error('Network error');
const blob = await res.blob();
return URL.createObjectURL(blob); // remember to revoke when done
}
// Usage
preloadAsBlob('/img/large.jpg').then(objectUrl => {
const img = document.createElement('img');
img.src = objectUrl;
document.body.appendChild(img);
// Later: URL.revokeObjectURL(objectUrl);
});
4. CSS background preloading
Use a hidden element or `@font-face`-style trick to force the browser to load background images defined in CSS. Good for decorative or background assets.
/* CSS */
.preload-bg {
background-image: url('/img/bg-large.jpg');
width: 0;
height: 0;
overflow: hidden;
position: absolute;
left: -9999px;
}
/* HTML injected by TypeScript */
const el = document.createElement('div');
el.className = 'preload-bg';
document.body.appendChild(el);
5. Lazy-loading with IntersectionObserver and prefetching ahead
Combine lazy-loading with a small prefetch window: when an element is near the viewport, start preloading its image so it appears instantly when scrolled into view.
function observeAndPreload(selector: string) {
const io = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const el = entry.target as HTMLElement;
const src = el.dataset.src;
if (src) {
const img = new Image();
img.src = src;
io.unobserve(el);
}
}
});
}, { rootMargin: '200px' });
document.querySelectorAll(selector).forEach(el => io.observe(el));
}
// Usage: <div class="lazy" data-src="/img/photo.jpg"></div>
observeAndPreload('.lazy');
6. Framework-specific APIs and server hints
Many frameworks provide resource-hint APIs or lifecycle hooks to preload resources during render. Use these when you want SSR-aware preloading or integration with the framework's router and rendering pipeline.
// Example: React (conceptual)
import { preload } from 'react-dom';
preload('/img/hero.jpg', { as: 'image' });
When to choose which method
- Image object — choose for quick, cross-browser prefetch of a handful of images.
- <link rel=preload> — choose for critical, above-the-fold images where browser priority matters.
- Fetch + blob — choose when you need to transform images or control binary data programmatically.
- IntersectionObserver — choose for lazy-loading with a prefetch window to balance bandwidth and UX.
- Framework APIs — choose when you need SSR integration or framework-level resource hints.