Modern web development requires responsive images across various devices and screen sizes. Loading unnecessarily large images for smaller screens wastes bandwidth and slows performance. This article explores three practical techniques to optimize image loading in React applications.
1. Native Lazy Loading
The simplest optimization is the browser-native loading="lazy" attribute. It defers image loading until the user scrolls near them, preventing all images from loading simultaneously during the initial page load.
export default function BlogPost() {
return (
<div>
<h1>My Blog Post</h1>
<img
src="/images/hero.webp"
alt="Hero image"
loading="lazy"
width={800}
height={400}
/>
<p>Content here...</p>
<img
src="/images/content.webp"
alt="Content image"
loading="lazy"
width={600}
height={300}
/>
</div>
);
}This allows non-critical resources to load only when needed, reducing network strain and improving perceived performance — especially on pages with many images.
2. Placeholder Images During Loading
Displaying a low-resolution placeholder while the full image loads asynchronously creates a smoother visual experience, particularly on slower connections.
import { useState } from "react";
export default function ProgressiveImage({ src, placeholder, alt, ...props }) {
const [loaded, setLoaded] = useState(false);
return (
<div style={{ position: "relative" }}>
{/* Low-res placeholder */}
<img
src={placeholder}
alt={alt}
style={{
position: "absolute",
inset: 0,
opacity: loaded ? 0 : 1,
transition: "opacity 0.3s ease",
filter: "blur(8px)",
}}
{...props}
/>
{/* Full-resolution image */}
<img
src={src}
alt={alt}
onLoad={() => setLoaded(true)}
style={{
opacity: loaded ? 1 : 0,
transition: "opacity 0.3s ease",
}}
{...props}
/>
</div>
);
}Usage:
<ProgressiveImage
src="/images/full-size.webp"
placeholder="/images/tiny-placeholder.webp"
alt="Product photo"
width={800}
height={400}
/>Once the full image loads, it replaces the blurred placeholder with a smooth fade transition.
3. Adaptive Image Loading
Different screen sizes require different image resolutions. Using the <picture> element with <source> tags and media / srcset attributes lets you serve the right image for the right viewport.
export default function AdaptiveImage({ alt, basePath }) {
return (
<picture>
{/* Extra large screens > 1200px */}
<source
media="(min-width: 1200px)"
srcSet={`${basePath}-xl.webp`}
/>
{/* Large screens 1000px–1200px */}
<source
media="(min-width: 1000px)"
srcSet={`${basePath}-lg.webp`}
/>
{/* Medium screens 500px–1000px */}
<source
media="(min-width: 500px)"
srcSet={`${basePath}-md.webp`}
/>
{/* Small screens < 500px */}
<img
src={`${basePath}-sm.webp`}
alt={alt}
loading="lazy"
style={{ width: "100%", height: "auto" }}
/>
</picture>
);
}Usage:
<AdaptiveImage
basePath="/images/product"
alt="Product photo"
/>The browser picks the first matching <source> and only downloads that variant — a 400px mobile screen never fetches a 2400px desktop image.
Combining All Three
You can combine all three techniques into a single reusable component:
import { useState } from "react";
export default function OptimizedImage({ basePath, placeholder, alt }) {
const [loaded, setLoaded] = useState(false);
return (
<div style={{ position: "relative" }}>
{!loaded && (
<img
src={placeholder}
alt={alt}
style={{ width: "100%", filter: "blur(8px)" }}
/>
)}
<picture style={{ opacity: loaded ? 1 : 0, transition: "opacity 0.3s" }}>
<source media="(min-width: 1200px)" srcSet={`${basePath}-xl.webp`} />
<source media="(min-width: 1000px)" srcSet={`${basePath}-lg.webp`} />
<source media="(min-width: 500px)" srcSet={`${basePath}-md.webp`} />
<img
src={`${basePath}-sm.webp`}
alt={alt}
loading="lazy"
onLoad={() => setLoaded(true)}
style={{ width: "100%", height: "auto" }}
/>
</picture>
</div>
);
}Conclusion
Implementing these three techniques — lazy loading, placeholder images, and adaptive variants — significantly improves React application performance and user experience across all devices. They are low-effort changes that directly impact Core Web Vitals scores, particularly LCP (Largest Contentful Paint) and CLS (Cumulative Layout Shift).