Why background images are slow to display – and how to make them appear faster

CSS background images tend to be loaded and displayed later than images referenced in <img> elements. As a rule, they’re given lower priority by the browser, and the CSS has to finish loading before they can be discovered (making them invisible to the browser preloader).

The following waterfall chart illustrates the issue:

waterfallnopreload

Late-loading background image – tested in WebPagetest using Canary

Sometimes, this can pose a problem:

1 Hero images

It’s quite common to find hero images (often in carousels) defined as background images. The result can be that one of the most important elements on the page is one of the last to be displayed. The simplest solution is just to switch to <img> elements. However, this might not be straightforward if media queries are used to deliver different images for different viewports.

2 Sprites

CSS sprites are an excellent way to reduce the number of requests/responses needed to build a page. However, using sprites can mean that important, above-the-fold background images such as buttons and icons end up getting displayed very late. Of course, you could say the same about virtually any background image, but I’ve singled out sprites because they may include important content and can’t easily be replicated as <img> elements (there are some workarounds, but they come with their own limitations).

Solutions

Hidden image element

If you need to keep your image as a background image, but you want it to load earlier, one option is to cheat: to add a hidden <img> element to your HTML. For example:

<img src="mysprite.png" style="display:none;">

In this way, visitors should already have the image in their cache by the time the browser discovers it’s needed as a background image.

This is simple and effective but it doesn’t address the issue of different versions of an image delivered according to a set of media queries, so while this might work well for sprites, it’s not very helpful in our first scenario.

Preloading

A similar, but neater, solution is the new preload specification. This is a way to tell the browser about resources that are going to be needed on the current page, so it can start downloading them before it would otherwise encounter them.

Support is very limited at the moment (currently only Chrome and Opera), but once it’s more widely adopted, it will allow you to achieve much thing as the previous solution, without the sense that you’ve mangled your markup. It’s worth noting that this isn’t just a cosmetic issue – it should also make your code more accessible and easier to maintain.

So, to preload a sprite, you would add something along the following lines to your document head:

<link rel="preload" href="mysprite.png" as="image">

Another advantage of this approach is that it will (it doesn’t yet) support media queries via the media attribute. This means it could be used in tandem with CSS media queries to deliver fast-loading responsive hero images as background images. The disadvantage of this, of course, is the extra maintenance involved, and a better alternative for hero images is suggested below.

However, for images that really have to be background images, such as sprites, this could be useful, especially if you maintain different sprites for different viewports.

The following waterfall charts show this and the previous technique in action:

waterfallpreloadimg

Getting background images to load earlier – tested in WebPagetest using Canary

Use responsive images in the HTML

If you’re using background images for hero images just to take advantage of media queries in the CSS, there is a better solution that’s enjoying increasing browser support. The responsive images specification (including the <picture> element, and the srcset and sizes attributes of the img element) features a number of ways to ensure that the most suitable image for the viewport is used without visitors having to download images they don’t need. It isn’t supported in Internet Explorer or, perhaps more importantly, in Opera Mini, but it does work in Edge as well as in most major desktop and mobile browsers.

The new specification means that you can replace the following CSS:

@media screen and (min-width: 480px) {
    #hero {
        background-image: url("big_picture.jpg");
    }
}
@media screen and (max-width: 479px) {
    #hero {
        background-image: url("small_picture.jpg");
    }
}

… with something like this in your HTML:

<div id="hero">
<picture>
    <source media="(min-width: 480px)" srcset="big_picture.jpg">
    <source media="(max-width: 479px)" srcset="small_picture.jpg">
    <img src="big_picture.jpg" alt="">
</picture>
</div>

Note that this isn’t necessarily the best way to do responsive images (for an excellent explanation, see Don’t use <picture> (most of the time) by Jason Grigsby). However, it does show that what once had to be done with CSS can now be achieved in HTML.

Conclusion

Very often it doesn’t matter if CSS background images load late. If they’re used for subtle design elements, people might not even notice as they’re dropped in after the main content. But sometimes background images are used to deliver things that visitors need before they can start using the website. Occasionally, there are compelling reasons for doing this. One of these reasons – using media queries to deliver responsive images – will become less important now that we can do the same thing in the HTML (without resorting to JavaScript). However, a few situations remain where background images are still the best choice – in these cases, consider using techniques such as hidden image elements or (in the future) preloading to make sure they’re loaded and displayed as soon as possible.

Leave a Reply

Your email address will not be published. Required fields are marked *