The CSS background-image property allowed us to do some amazing things, but in most cases, it’s time to leave it behind.
Many of us have cut our teeth using the CSS background-image property to do a wide variety of things. To many, it’s like an old friend, but it’s one we should consider saying goodbye to.
The real problem isn’t the CSS background-image property itself. Rather it is that it’s been used in places where it really shouldn’t be, such as for the main CTA hero image or for UI images.
Used improperly, background-image can be an anti-pattern. Are there legit use cases for background-image? Of course.
However, there are some serious downsides to using the CSS background-image property, and more importantly, we have better ways to implement images in our browsers today.
Let us not speak of the days of dancing hamster backgrounds
So let’s jump right in and talk about the downsides of using the CSS background-image property, and then what we can use instead.
Here are a few reasons why using the CSS background-image property can be bad:
1. Bad for SEO
Images that are in CSS background-image properties are not crawled or indexed by Google. No big deal right? Well, check out this quote from the How to Rank in Google Image Search Moz.com article:
…a third of all searches performed in Google are for images and 12.5% of SERPs show Image Pack results…
Let that sink in. One third or 33% of searches performed in Google are for images. If the image is relevant at all to the subject of the page, or your client’s business (and if its not, it probably should be), you absolutely want to be indexed.
If you use background-image you miss out on this, nor can you have an alt="" description of the image to give Google a description & context for the image.
Browsers do not provide any special information on background images to assistive technology. This is important primarily for screen readers, as a screen reader will not announce its presence and therefore convey nothing to its users.
Even if screen readers didn’t completely ignore images, there’s no alt="" text that can give a useful description of the image or context for it.
Pro Tip: If you actually want a screen reader to skip an image (maybe it’s just a design element), just have an empty alt tag, e.g.: alt="" (the use of the role="presentation" property is not optimal, as it violates the first rule of ARIA).
3. Bad for Performance
How could the background-image property possibly be bad for performance? Because typically just one image is used for the background-image property regardless of the device screen width or resolution.
What we ideally want is to be able to use responsive images so that the browser can load a different sized image depending on the device’s screen width or resolution.
This makes a massive difference on mobile device or low bandwidth connections, as well as on the processing power needed to scale images. Check out the Post-Mortem: Applied Image Optimization article for details.
On a typical site, images will by far be the largest chunk of the data sent. We should optimize them.
While you can do this via @media queries with CSS background-image it ends up getting really sticky if you need to change the image or rename it or version it.
The reason it gets sticky is that the image is embedded in your CSS, which is typically built by a build process that is not in the client’s control.
So because it’s hard, people usually just punt on it and have one version of the image for all device screen sizes and resolutions.
Add in the desire to use next-generation forms like webp as a progressive enhancement, and things get really icky with background-image.
Your browser can’t start downloading an image until it has downloaded & parsed the CSS
Another performance benefit is that if your images are embedded in your CSS via background-image, the browser has to download and parse your CSS before it can request the images.
This means your hero image is going to be lower down the download queue, and slower to load. If you do your images via HTML elements instead, the browser can request them much faster, sometimes even before the stylesheets start downloading.
4. Bad for CMS’s & CDN’s
Let’s say that I have a hero image on my website that I implemented as a <div> with the image set via background-image and then my website takes off!
People from all over the world are hitting my site, and I want to use a CDN to deliver these resources to them optimally.
If I have the image URL embedded in my CSS, I’m going to have to do a global search and replace for all of my images to switch over their URLs from local URLs to my CDN URLs.
If you prefaced all of your image URLs with CSS variables, good for you! But most people don’t, and having to build and deploy a new CSS asset bundle whenever you want to change your images seems… excessive. But it’s either that, or dynamically generating inline CSS… but let’s not go there.
It’s be much easier to just let your CMS handle the URL prefix, but that can get gross if you’re doing it via CSS background-image, necessitating the generation of inline styles and other shenanigans.
Putting image URLs in CSS always feels like “hardcoding it” to me now.
So when is it good?
So we’ve been talking all about the downsides, but when is it actually good to use the CSS background-image property?
It may seem blatantly obvious, but if you have the case where you actually have an image as a purely decorative image background… that’s when to use background-image!
It’s good to use background-image when you have an actual background image
If you are doing that, consider using image-set() along with background-image to mitigate the performance issues mentioned above.
It’s not that the property is inherently bad, it’s that it has been shoehorned into doing things it probably isn’t the best choice for.
Use <picture> instead!
I think a large reason people use background-image is because of the CSS background-size: cover; property. It lets you make sure that an image “covers” the thing its attached to in an aesthetically pleasing way.
The object-fit property is here to save the day!
If you use the semantic HTML element <picture> with the object-fit property you get the same benefits of background-size: cover; but you also get:
- SEO-friendly images
- Accessibility friendly images (just use the alt="" property)
- Works great with CMS-generated image links & CDNs
- Works great with srcset for optimized images
- Can use <source> for next-generation image formats like .webp
It’s really not hard to do, and the benefits are numerous! The object-fit property tells the image or video how it should fit into the parent container. You’d just change this:
<div style="background-image: url('/some/man-with-a-dog.jpg'); background-size: cover;"> </div>
<picture> <img src="/some/man-with-a-dog.jpg" alt="Man with a dog" style="object-fit: cover;" /> </picture>
Obviously in both cases, you probably wouldn’t be using inline styles, but hopefully you can see how much more flexible of an approach we have if we ditch the background-image property and use actual semantic HTML elements like <picture> and <img>.
We can then easily add responsively sized images via srcset. We can also easily add a next generation image formats like .webp for the browser to choose from via <source> as well.
Then we also get to take advantage of things like native image lazy loading in Chrome and other browsers as it gets implemented.
Here’s what an final block of code that does all the things might look like:
<picture> <source srcset="/some/_1170x658_crop_center-center/man-with-a-dog.webp 1170w, /some/_970x545_crop_center-center/man-with-a-dog.webp 970w, /some/_750x562_crop_center-center/man-with-a-dog.webp 750w, /some/_320x240_crop_center-center/man-with-a-dog.webp 320w" sizes="100vw" type="image/webp" /> <source srcset="/some/_1170x658_crop_center-center/man-with-a-dog.jpg 1170w, /some/_970x545_crop_center-center/man-with-a-dog.jpg 970w, /some/_750x562_crop_center-center/man-with-a-dog.jpg 750w, /some/_320x240_crop_center-center/man-with-a-dog.jpg 320w" sizes="100vw" /> <img src="/some/man-with-a-dog-placeholder.jpg" alt="Man with a dog" style="object-fit: cover;" loading="lazy" /> </picture>
This gives us
- lazy loading if the browser supports it (otherwise it does nothing)
- webp if the browser supports it (otherwise it does nothing)
- a srcset of optimized responsive images that the browser can choose from.
SEO friendly, progressive enhancement, accessibility and performance all rolled up into one nice tight little bundle.
And all of the images can be coming automatically sized from our CMS via tools like ImageOptimize, making it easy to point to a CDN or Amazon S3. Finally, you can do some very creative cropping of images with object-fit.
You can also use the object-position property (analogous to background-position for background images) to position your image as needed.
Out with the old, in with the new
Browser support for <picture> and srcset is pretty fantastic (with a polyfill if you need it), and so is browser support for object-fit (also with a polyfill if you need it). Another nice pattern to use is this via polyfill.io
So the time is now to ditch background-image from your toolset (except in the rare cases where you actually want a background image).
Code using best practices for the overwhelming majority now, then use progressive enhancement and a polyfill when necessary. Don’t be shackled to the past for what amounts to a very small percentage of your visitors.
Don’t use a content image as decoration; use semantic HTML content image elements
I think you’ll find that you end up building websites that are more SEO friendly, more accessible, and more performant… as well as just feeling more satisfying to write in a semantic HTML way.
The CSS background-image property was never meant to be used for your hero images or UI elements. So now that better ways to do it are here, let’s use ‘em.
If this is gotten you excited about optimizing images, check out the excellent images.guide for a comprehensive write up.
Enjoy your object-fit!