When to use img vs CSS background-image?
Use img for images that are content and CSS background-image for decoration. What each one gives you for accessibility, SEO, LCP and responsive images.
Ask whether the page would lose information without the image. If it would, the image is content, so use <img>. If it's decoration, like a texture, a pattern or a shape behind a heading, use background-image.
<!-- Content: a product photo -->
<img
src="/products/chair-800.jpg"
alt="Oak dining chair with a woven seat"
width="800"
height="800"
>/* Decoration: a dotted pattern behind a section */
.newsletter {
background-image: url('/patterns/dots.svg');
background-repeat: repeat;
}Product photos, avatars, article images, logos, charts and screenshots are content. Background textures, ornaments and blurred color shapes are decoration. When I'm not sure, I use <img>.
What img gives you
- Alt text. Screen readers read the
alt. A CSS background has no text alternative. - Image search. Search engines index
<img>elements. CSS background images don't show up in image search. - Responsive images.
srcsetandsizeslet the browser download a file that fits the screen. - Lazy loading with
loading="lazy", no JavaScript needed. - Priority with
fetchpriority="high"for the image that is your Largest Contentful Paint (LCP) element. - Early discovery. The browser's preload scanner finds
<img>in the HTML before the CSS has loaded. A CSS background is only found after the stylesheet is downloaded and the browser has worked out which elements use it, which slows down LCP when that image is the hero. - Printing. Browsers don't print background images by default.
- No layout shift. The
widthandheightattributes let the browser reserve the space before the image loads.
A hero image as <img>:
<img
src="/hero-1200.jpg"
srcset="
/hero-800.jpg 800w,
/hero-1200.jpg 1200w,
/hero-2000.jpg 2000w
"
sizes="100vw"
width="2000"
height="1000"
alt="Designers sketching app screens around a table"
fetchpriority="high"
>Don't add loading="lazy" to the LCP image. Lazy loading is for images further down the page.
What background-image gives you
- Layers. Stack gradients and images in one declaration, for example a dark overlay that keeps text readable.
- Repeating patterns with
background-repeatandbackground-size. - Different resolutions with
image-set(). - Decoration that stays out of the HTML, so screen readers never see it.
.promo {
background-image:
linear-gradient(rgb(0 0 0 / 0.6), rgb(0 0 0 / 0.2)),
image-set(
url('/shapes.png') 1x,
url('/shapes@2x.png') 2x
);
background-size: cover;
}"But I need cover"
background-size: cover used to be the main reason to put photos in CSS. <img> does the same with object-fit: cover, and you can still place text on top:
.hero {
position: relative;
isolation: isolate;
}
.hero img {
position: absolute;
inset: 0;
z-index: -1;
width: 100%;
height: 100%;
object-fit: cover;
}isolation: isolate keeps the image behind the text but inside .hero. Use object-position to choose which part of the photo stays visible when it's cropped. There's more in how to fit an image into its container.
If an LCP image really has to be a CSS background, preload it so the browser finds it early:
<link rel="preload" as="image" href="/hero.jpg" fetchpriority="high">Decorative images in HTML
Sometimes a decorative image ends up in the HTML anyway, for example from a CMS. Give it an empty alt="" so screen readers skip it. Don't leave alt out entirely, because some screen readers then read the file name.
The reverse doesn't work well: a content image in CSS patched with role="img" and aria-label on a <div> gets a text alternative, but it still misses everything else in the <img> list above.