Image Optimization Techniques
In this page:
Serving Appropriately-Sized Images
Loading a huge, full-resolution image and just displaying it small via CSS still downloads the FULL file size — the browser doesn't magically shrink the download. Serving an image already sized close to its actual display dimensions avoids wasting bandwidth on pixels that are never actually shown.
Note: For responsive designs, the <picture> element or srcset attribute can serve different image sizes for different screen widths automatically.
Warning: A 4000px-wide photo displayed in a 200px-wide thumbnail still costs the user the full 4000px download — CSS resizing doesn't reduce the actual file transfer.
Example: Serving Appropriately-Sized Images
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function ProductCard() {
return <img src="product-thumb-400w.jpg" width="200" height="200" alt="Product" />;
}
ReactDOM.createRoot(document.getElementById('root')).render(<ProductCard />);
</script>
</body>
</html>
Lazy Loading Off-Screen Images
The native 'loading="lazy"' attribute tells the browser to defer downloading an image until it's about to scroll into view, rather than loading everything upfront. This is a simple, built-in optimization needing no extra library for images below the initial visible viewport.
Note: Don't lazy-load images that are immediately visible on page load (like a hero banner) — that would actually delay showing them.
Warning: Lazy-loading images that ARE immediately visible can cause a noticeable delay before they appear, since the browser waits before starting the download.
Example: Lazy Loading Off-Screen Images
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Gallery() {
const images = ["photo1.jpg", "photo2.jpg", "photo3.jpg"];
return (
<div>
{images.map((src, i) => (
<img key={src} src={src} loading={i === 0 ? "eager" : "lazy"} alt={"Photo " + (i + 1)} width="150" />
))}
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<Gallery />);
</script>
</body>
</html>
Preventing Layout Shift
Without explicit width/height, an image's space in the layout is unknown until it finishes loading, causing surrounding content to visibly jump around as each image pops in. Setting width and height (or a matching aspect-ratio) reserves the correct space upfront, keeping the layout stable.
Note: Even if the actual displayed size is controlled by CSS, setting width/height attributes still helps the browser reserve the correct aspect ratio early.
Warning: Missing width/height on images is one of the most common causes of a poor Cumulative Layout Shift (CLS) score, a real, measured web performance metric.
Example: Preventing Layout Shift
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function Article() {
return (
<div>
<p>Some text before the image.</p>
<img src="diagram.jpg" width="400" height="300" alt="Diagram" style={{ display: "block" }} />
<p>Text after — doesn't jump around once width/height are set.</p>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<Article />);
</script>
</body>
</html>
- Serving a full-resolution image (like a 4000px-wide photo) when it's only ever displayed at 400px wide.
- Not using lazy loading for images far down the page, forcing the browser to download them before they're ever seen.
- Missing width/height attributes on images, causing layout shift as each image loads in and pushes content around.
- Serving appropriately-sized images (matching their actual display size) avoids wasted bandwidth.
- The native loading=lazy attribute defers loading off-screen images until the user scrolls near them.
- Setting explicit width/height (or aspect-ratio) prevents layout shift as images load in.
- Modern formats like WebP/AVIF offer better compression than older formats like JPEG/PNG for the same visual quality.
loading=lazy is supported in all modern browsers; WebP/AVIF support is broad but always provide a fallback for very old browsers if needed.
Chapter Quiz — Complete all 13 topics to unlock
0/13 topics done
Complete these topics first:
- Why Performance Matters
- Optimizing with React.memo
- Avoiding Unnecessary Re-renders
- Using the React Profiler
- Analyzing Bundle Size
- Image Optimization Techniques
- React as a PWA
- Creating a Production Build
- Environment Variables in React
- Deploying to Netlify
- Deploying to Vercel
- Basic CI/CD for React Apps
- SEO Basics for React SPAs