← Back to React Course | Chapter 14: Performance & Production Deployment | Lesson 6 of 13

Image Optimization Techniques

Image optimization is like packing a suitcase efficiently — you want the photo to still look good, just without taking up way more space than it needs to.

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

markup
<!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

markup
<!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

markup
<!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>
Common Mistakes
  1. Serving a full-resolution image (like a 4000px-wide photo) when it's only ever displayed at 400px wide.
  2. Not using lazy loading for images far down the page, forcing the browser to download them before they're ever seen.
  3. Missing width/height attributes on images, causing layout shift as each image loads in and pushes content around.
Chapter Summary
  • 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.
Browser Support

loading=lazy is supported in all modern browsers; WebP/AVIF support is broad but always provide a fallback for very old browsers if needed.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.