TLDR: just show me the code already

A few days ago, I was testing the speed of swimburger.net using PageSpeed Insights and noticed that hidden images on mobile devices were still being loaded.

Google’s recommendation for this is to lazy-load images and only load them when necessary using JavaScript. This makes sense in cases where the images aren’t visible due to them being ‘below the fold’ aka you need to scroll down to see them.

PageSpeed Insight recommendation:



But in my case the images are simply hidden forever using CSS media-queries so this was a total waste of performance and precious mobile internet data. Lazy-loading seems a little overkill for this simple scenario so instead of adding JavaScript and making the site harder to index, I employed HTML5’s picture and source tags to solve the job.

Why do browsers load images even when they’re hidden? #

You may have written something like below before, and naively expected the browser not to load the image on mobile devices because it’s hidden by a CSS media query.

<!DOCTYPE html> < html > < head > < meta charset = "utf-8" > < meta name = "viewport" content = "width=device-width" > < style > @ media screen and (max-width: 768px) { img { display : none ; } } </ style > < title > Hidden images on mobile optimization </ title > </ head > < body > < img src = "https://placekitten.com/200/300" alt = "Cute kittens" width = "200" height = "300" > </ body > </ html >

See it in action on jsbin.

Even though the image is hidden on mobile screen widths, the image is still being loaded. You can validate this by using Chrome’s devtools under the ‘Network’ tab.

So why do browsers load images even when they’re hidden? Simply explained:

The browsers parses HTML first and as it encounters external resources it will start requesting them. At this point CSS isn’t even being applied.

When you have hidden images, incidentally you are slowing down your page and wasting the visitors’ mobile data. The best performing HTTP request is one not sent at all.

Solving useless image request using plain HTML5 #

The HTML5 spec introduced many new elements, including the picture and source tag. With these tags you can offer alternative versions of an img. The browser will determine which version it will show based on various factors such as:

Support for image format (fe: webp images)

Screen resolution (fe: high res phones and retina displays will download a higher resolution)

Media query (partial media query support)

You can learn more at Mozilla’s documentation on the picture element.

Browser support: the picture element is supported by all major browsers with the exception of IE. If not supported the browser simply falls back to using the img tag.

Taking advantage of this knowledge, we can combine the power of the picture element, media queries, and the smallest possible base64 data image.

<!DOCTYPE html> < html > < head > < meta charset = "utf-8" > < meta name = "viewport" content = "width=device-width" > < style > @ media screen and (max-width: 768px) { img { display : none ; } } </ style > < title > Hidden images on mobile optimization </ title > </ head > < body > < picture > < source srcset = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" media = "(max-width: 768px)" > < img src = "https://placekitten.com/200/300" alt = "Cute kittens" width = "200" height = "300" > </ picture > </ body > </ html >

If the viewport width is smaller than 768px:

the browser will use the alternative source instead of the img src and

the alternative source is the smallest base64 data img possible afaik

Before: https://jsbin.com/lukowas/edit?html,output

After: https://jsbin.com/zazoren/edit?html,output

As a result, when the browser is parsing the DOM it only loads the data image instead of making another HTTP request.

Now PageSpeed Insights doesn’t report on offscreen images anymore:



Alternative solutions:

Use CSS background images (not SEO friendly)

Use an actual 1x1 img served from your server which can result in smaller HTML (more performant with very large amount of images, see SO answer)

Lazy loading (less SEO friendly because it requires JavaScript)

Performance tools:

If you know any other optimizations or how to improve on this solution, let’s discuss in the comments :)