aspectfit

CSS object-fit: contain, cover and the other three

object-fit decides how an image or video fills its own element box when the two have different shapes. contain is aspect fit, cover is aspect fill, and the initial value, fill, stretches.

The five values

Here is the same 1200 × 800 image in a 300 × 300 box with each value. Hatched areas are empty; dashed outlines are parts of the image that are clipped away.

stretch / fill
300300300300
fit / contain
3003003002005050
fill / cover
300300450300
none
3003001200800−450−450−250−250
scale-down
3003003002005050
fill, contain, cover, none, scale-down: a 3:2 image in a square box.
ValueScaleResult
fillwidth and height independentlyFills the box exactly; distorted when the ratios differ. The initial value.
containmin(boxW / imgW, boxH / imgH)Whole image visible, empty space on two sides.
covermax(boxW / imgW, boxH / imgH)Box fully covered, image clipped on two sides.
none1Image at its natural size, clipped if larger than the box.
scale-downmin(1, contain scale)Like none or contain, whichever gives the smaller image. It shrinks, never enlarges.

For the example, contain draws the image at 300 × 200 with 50 px empty above and below. Cover draws it at 450 × 300 and clips 75 px from each side. Scale-down picks contain here, because the natural size (1200 × 800) is larger than the box.

CSS
.card img {
  width: 100%;
  aspect-ratio: 4 / 3;     /* gives the box a shape of its own */
  object-fit: cover;       /* fill the shape, crop the overflow */
}

object-fit needs a box with its own shape

An image only gets cropped or letterboxed if its element box has a different ratio from the image. With the common responsive rule img { max-width: 100%; height: auto; } the box always takes the image's own ratio, so every object-fit value looks the same. Give the box both dimensions, or one dimension plus aspect-ratio:

CSS
/* fixed size */
.avatar { width: 96px; height: 96px; object-fit: cover; border-radius: 50%; }

/* fluid width, fixed shape */
.thumb { width: 100%; aspect-ratio: 1; object-fit: cover; }

/* fill a grid cell: the cell sets the size */
.gallery { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-auto-rows: 200px; }
.gallery img { width: 100%; height: 100%; object-fit: cover; }

Rounded corners work with cover without any wrapper: border-radius clips the image's content too.

Moving the image with object-position

object-position takes the same syntax as background-position; the initial value is 50% 50%, which is why contain and cover center by default. Percentages work like an anchor: the offset is the leftover space times the percentage.

Formula
x = (boxWidth  - drawnWidth)  * percentX
y = (boxHeight - drawnHeight) * percentY

With cover in the square example the leftover width is 300 − 450 = −150 px. At 0% the image starts at x = 0 and all 150 px are cut from the right; at 100% they are cut from the left; at 50% 75 px go from each side.

fill / cover
300300450300−75−75
cover with object-position: 50% 50% (the default).
CSS
.portrait-in-banner {
  width: 100%;
  height: 240px;
  object-fit: cover;
  object-position: 50% 20%;   /* keep the upper part, where faces usually are */
}
.logo {
  width: 160px;
  height: 80px;
  object-fit: contain;
  object-position: left center;  /* letterbox, but hug the left edge */
}

Edge offsets work too: object-position: right 12px bottom 8px places the image 12 px from the right and 8 px from the bottom.

The aspect-ratio property

aspect-ratio gives any box a preferred shape: aspect-ratio: 16 / 9, aspect-ratio: 1, or aspect-ratio: auto 4 / 3 (use the element's natural ratio if it has one, otherwise 4:3). It only takes effect when at least one dimension is automatic; if you set both width and height, the ratio is ignored.

Current browsers also use an image's width and height attributes to reserve space before the file has loaded, by mapping them to aspect-ratio: auto width / height. That is why writing the attributes, together with height: auto in CSS, prevents layout shift:

HTML
<img src="photo-800.webp" width="1200" height="800" alt="Harbor at dusk"
     style="width: 100%; height: auto">

For boxes with text inside (not images), the ratio gives way to the content: if the text needs more height, the box grows. Add overflow: hidden or min-height: 0 if the shape must hold.

Video and object-fit

Browsers' default stylesheet gives <video> object-fit: contain, which is why a 16:9 video in a square player shows black bands. For a background video that should fill its section, switch to cover:

CSS
.hero video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

background-size: the same idea for backgrounds

object-fit only applies to replaced elements such as <img> and <video>. Setting it on a <div> does nothing. For CSS background images the equivalents are background-size: contain and background-size: cover, with background-position doing the job of object-position:

CSS
.banner {
  aspect-ratio: 3 / 1;
  background: #dfe6ee url("banner.webp") center / cover no-repeat;
}

With contain, remember no-repeat: the default background-repeat: repeat tiles the image into the empty bands.

Responsive images with cover

With srcset and sizes, the browser picks a file based on the width you declare in sizes. With cover in a box that is relatively taller than the image, the image is drawn wider than the box, so the declared width understates what is needed. Rendered width under cover is max(boxWidth, boxHeight × imageRatio): a 3:2 image covering a 400 × 400 box is drawn 600 px wide. If a different crop is wanted on small screens, use <picture> with separately cropped files rather than relying on object-position alone.

Common problems

  • Nothing changes. The box has the image's own ratio (see object-fit needs a box with its own shape), or the rule targets a wrapper instead of the img.
  • Image ignores the grid or flex height. Give the image height: 100% (the cell needs a definite height) or an aspect-ratio; in a flex column, min-height: 0 on the item lets it shrink.
  • Switching contain to cover does not animate. object-fit is a discrete property. Animate a transform: scale() instead.
  • Support. object-fit and object-position work in all current major browsers. Internet Explorer never supported them.

To see the numbers for your own box and image, use the visualizer; the CSS tab shows the matching rule. The aspect fit vs aspect fill guide explains the same math from the iOS side.

Questions

What is the difference between object-fit contain and cover?

contain scales the image by the smaller of the two ratios so the whole image is visible and empty space appears on two sides. cover scales by the larger ratio so the box is completely filled and the image is clipped on two sides. Both keep the image's aspect ratio.

What is the default value of object-fit?

fill. An img with both width and height set stretches to that box unless you change object-fit. Browsers' default stylesheet sets object-fit: contain on video elements.

Why does object-fit: cover do nothing?

The image's box has the same shape as the image, usually because height is auto. Give the img a fixed height, height: 100% inside a sized container, or an aspect-ratio.