RWD Responsive Images

The two-line CSS rule img { max-width: 100%; height: auto; } keeps images from breaking a layout, but it doesn't shrink how much data a phone actually downloads.

Why Images Don't Behave Like Text

A paragraph of text automatically reflows to fit whatever width it's given, but an <img> element carries an intrinsic size baked into the file itself. Drop a 1600-pixel-wide photo into a 320-pixel-wide phone screen with no CSS at all, and the image stays 1600 pixels wide, pushing the whole page into horizontal scrolling.

The Problem

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

<img src="skyline.jpg" alt="City skyline at dusk">

/* With no CSS rule, this renders at its
   native file width -- even inside a
   320px-wide phone viewport. */

</body>
</html>

The fix is a short rule that's worth memorizing, because you'll reach for it on nearly every project:

The Fix

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
img {
  max-width: 100%;
  height: auto;
  display: block;
}
</style>
</head>
<body>

</body>
</html>
  • max-width: 100% caps the image at the width of its containing element, but still lets it be narrower.
  • height: auto recalculates the height to match whatever width the browser lands on, so the original aspect ratio is preserved instead of the image stretching or squashing.
  • display: block removes the few pixels of gap that inline images leave underneath them, caused by normal line-height spacing.
Note: max-width only shrinks images down -- it never enlarges them past their real pixel dimensions. Size a 200px-wide thumbnail file up to fill a 600px container and it will look soft and blurry. Always start with a source image at least as large as its biggest expected display size.

Fluid Images Need a Fluid Container

A responsive image rarely works in isolation -- it inherits its available width from whatever wraps it. The max-width: 100% rule only has something meaningful to respond to if its parent is itself allowed to resize, whether through percentages, a fluid grid, or a flex layout.

Image Inside a Responsive Card

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
  .card {
    width: 100%;
    max-width: 360px;
  }
  .card img {
    max-width: 100%;
    height: auto;
    display: block;
  }
</style>
</head>
<body>

<div class="card">
  <img src="mountain.jpg" alt="Mountain range at sunrise">
  <p>Mountain Range Trail</p>
</div>

</body>
</html>

What max-width Doesn't Solve: File Weight

This CSS rule only ever controls how large the image is drawn on screen -- it does nothing to change how large the downloaded file is. A phone showing that photo at 343px wide still fetches the exact same 1600px-wide file a desktop monitor would, wasting data and slowing the page down on the connections that can least afford it. Solving that problem needs a different tool: the srcset attribute and the <picture> element, covered next, which let the browser choose a differently sized (or differently cropped) file for each situation instead of resizing one file with CSS alone.