Responsive design is an absolute necessity in today’s web. More users are visiting websites from their mobile devices than ever before, and if your website doesn’t look good on when viewed from that device, they’ll bounce. Unfortunately, many themes and frameworks achieve responsive design by using hidden images on mobile devices or tablets.

Ok, so what’s the problem?

Did you know that if you’re using CSS to hide images based on the screen size that they’re still being downloaded by the visitor’s browser?

This is a huge deal because your visitors are still on the hook for the time it takes to download each of these images (as well as the data required), and they don’t even get to see them!

This is a common problem for many responsive frameworks, such as Bootstrap CSS.

Thankfully, there are 4 ways that we can fix this issue. Implementing one of these options will ensure that your visitors are no longer downloading images they never see. Plus, you’ll see a nice improvement in your mobile page speed scores!

Using <picture> with srcset

The main reason that a developer might choose to hide certain images for the mobile version of a site is to serve images that are better scaled for mobile.

The large HD image you use for a hero image on the desktop version of your site isn’t going to look near as good on a small screen. Even if it looks fine, making the browser load that large image and then scaling it down to a mobile screen is not a performant solution.

However, we want to avoid hidden images, so we don’t want to put 2 images into our HTML and hide one based on which screen size is being used. Below is an example of what that code would look like.

<img class="display-block display-md-none" src="{{ secure_asset('assets/images/banner-mobile.jpg') }}" />
<img class="display-none display-md-block" src="{{ secure_asset('assets/images/banner-desktop.jpg') }}" />

Since the browser starts to download any <img> elements before the main parser has started to load and interpret the page’s CSS and JavaScript, each of these images are downloaded, even though we will only ever display one of them at a time.

A much better way to handle this would be to use <picture> and show the browser that you have provided separate image options based on screen size.

<picture>

<source srcset="{{ secure_asset('assets/images/banner-   desktop.jpg') }}" media="(min-width: 768px)" />
<img src="{{ secure_asset('assets/images/banner-mobile.jpg') }}" />

</picture>

The <picture> tag works in the same way that CSS media queries do. In the above code, the <img> tag is used as the default. However, if the browser detects a screen size 768px or larger, the top image is downloaded and used instead.

Since the browser knows immediately what screen size it’s being loaded on, it can make an immediate decision, and only download one of the images.

Other advantages of <picture>

This tag also offers other web performance advantages by giving us an easy way to handle browser compatibility.

There are several next-gen image formats (such as WebP and AVIF) that can offer performance and high quality at the same time. However, browser support is often slow going.

For example, AVIF is really only supported in Chrome. You can still take advantage of it! <picture> allows you to serve that format to those that can use it, and set alternatives for everyone else.

Caniuse.com for AVIF support
<picture>
  <source type="image/avif" srcset="pyramid.avif">
  <source type="image/webp" srcset="pyramid.webp"> 
  <img src="pyramid.png" alt="regular pyramid built from four equilateral triangles">
</picture>

<picture> lets us continue catering to everyone. You can supply MIME types inside type attributes so the browser can immediately reject unsupported file types while serving the best image format where possible.

With this method, everyone is being served the most performant images available to them.

Making the image a div with a background

As we mentioned, the browser will pull all HTML <img> elements before beginning to parse CSS and JS. However, we can get around this if we use CSS to set our image as a background image.

To hide images using this method, simply set your image as the background image for a div or section. From there, you can use a media query to hide that div based on the size of the screen. W3Schools has a good example of what this looks like on their website.

The image will not load in this situation, because there is no <img> tag. The CSS is being parsed, and the browser can see the scenarios where that background image should be loaded.

Lazy loading (native or with a plugin)

In nearly every instance, you should be lazy loading your images regardless of whether you’re loading the desktop or the mobile version of your site. You can accomplish this with a plugin if you’re using a CMS like WordPress (though WordPress also offers native loading now). Native lazy loading is also widely available at a browser level as well.

Lazy loading also works nicely when working with responsive design. Since images that are lazy loaded are only loaded once the visitor scrolls to them, images that are hidden will not be loaded. The only catch is that this will not work for images that are in the initial viewport (above the fold). These images get downloaded immediately regardless of whether they are hidden or not.

For this reason, lazy loading will likely not be a standalone fix. However, it works wonders when used in conjunction with the other fixes we’ve discussed.

Content-visibility

A relatively new option introduced in Chrome 85, the content-visibility CSS property has the potential to make a huge difference in page performance.

Web.dev goes into great detail about this new property, and we encourage everyone to check it out. Content-visibility works very much like lazy-loading does. However, it can be used for entire sections of your site, not just images.

What if you want to keep the content unrendered regardless of whether or not it is on-screen, while leveraging the benefits of cached rendering state? Enter: content-visibility: hidden.

This gives you more control, allowing you to hide an element’s contents and later unhide them quickly.

Content-visibility: hidden hides the element while preserving its rendering state, so, if there are any changes that need to happen, they only happen when the element is shown again (i.e. the content-visibility: hidden is removed).

Source: Web.dev

No need to use hidden images anymore!

It’s important to seek out win-win scenarios in web performance. Implementing one of these alternatives to hiding your images for responsive designs is one such win-win. You get a faster loading website, across all devices. Your visitors get to enjoy your site without using their mobile data to load images they never see.

We’re excited to see how browser support develops for some of the newer alternatives. Play around with these options and see which one works best for your site!

Are you using Speedy Site?

Website speed optimization with speedy.site ensures your WordPress site is loading fast throughout the day, around the world, and on various devices as well as passing Google's code web vitals.