Skip to content
Daniel Shaw · WordPress Developer Wellington, New Zealand

Managing responsive background images with WordPress

This is a solution allowing for per-page responsive background images without setting hard-coded image paths in a stylesheet. This example assumes the image is set as the featured image for a page.

The two display issues we’ll solve here are both context-based: how to avoid serving an overly-heavy image to smaller screens, and how to adapt the image aspect-ratio to better accommodate the user’s current screen width.

The workhorse image

I regularly see images destined for the browser asked to perform an impossible task: dimension, format and aspect-ratio are each expected to be completely fluid while maintaining the integrity of the visual content.

A common device is an image with text overlaid—for example, imagine a banner at the top of a page. On a wider screen this will typically be heavily landscape-biased, but what happens when the available screen width is scaled down?

When viewed on a smaller screen the image will, by default, maintain its aspect ratio and be reduced to a short-&-wide strip. In conflict with this, text will flow downwards as line-lengths are considerably constrained by shrinking horizontal space.

This means the height of the image cannot be pre-determined; instead, it will always be determined by the dynamic height of the text block.

Background-size vs. object-fit

Choosing whether to use <img> with object-fit or a background image with background-size comes down to how the image needs to perform—browser support for object-fit aside—and layout requirements imposed by the design.

background-size and object-fit can be configured to instruct images to seep into all available space while preserving aspect-ratio. Both properties do this at the expense of controlling the visible width and/or height of an image. For more precision in how images are displayed, cropping becomes an ally.

The approach here works well where there's no SEO benefit lost by using a background image, and regardless of whether the image is displaying meaningful visual content or simply to provide "texture".

Defining alternate images

First, it’s necessary to tell WordPress to prepare new images for display. All images uploaded in the WordPress admin—via the Add Media button, Add Featured Image link, or directly in the Media section—can be easily targeted for resizing and cropping. In this example the image would be uploaded via the Add Featured Image link.

Let's assume two image sizes are required, large and small versions:

/* * Enable support for Post Thumbnails on posts and pages. * @link https://developer.wordpress.org/themes/functionality/featured-images-post-thumbnails/ */ add_theme_support( 'post-thumbnails' ); if ( function_exists( 'add_image_size' ) ) { add_image_size( 'banner-l', 1600, 600, true ); add_image_size( 'banner-s', 500, 500, true ); }

The first attribute is the unique name for the image; the second and third attributes are the width and height, respectively; the fourth attribute ensures the image is cropped exactly to the specified width and height (as opposed to resizing to one or the other dimension).

Source image

Using the image banner.jpg below as input, two new images will be automatically generated per the white rectangles.

The Famous Mayan Bee
"The Famous Mayan Bee" is used here under the Creative Commons CC0 1.0 Universal Public Domain Dedication.

WordPress-generated images are automatically named according to this convention: filename-[width]x[height].jpg. For example, the large version will be banner-1600x600.jpg, based on a source image called banner.jpg.

Retrieving the images

All we need to know to retrieve each image size is its URI, something wp_get_attachment_image_src() can provide. This example uses a featured image to store the target, so get_post_thumbnail_id() is used to retrieve the image ID.

The following function finds each variant of the target image and retrieves a URI. This is used to construct CSS media queries that will be inlined in the head of the page, using wp_add_inline_style(). An important caveat for using wp_add_inline_style() is the first argument must reference a stylesheet that is already loaded:

/** * Prints inline CSS to the header for a responsive background banner image * @link https://developer.wordpress.org/reference/functions/wp_add_inline_style/ */ function the_theme_css() { // wp_add_inline_style must reference an existing stylesheet wp_enqueue_style( 'theme-css', get_template_directory_uri() . '/style.css' ); // Get the featured image $featured_id = (int)get_post_thumbnail_id(); if ( !empty ( $featured_id ) ) { // Get the cropped versions of the image $banner_s = wp_get_attachment_image_src( $featured_id, 'banner-s' )[0]; $banner_l = wp_get_attachment_image_src( $featured_id, 'banner-l' )[0]; // Describe CSS to be set inline $page_banner_css = '.banner{background-image:url(' . esc_url ( $banner_s ) . ');}@media(min-width: 75em){.banner{background-image:url(' . esc_url( $banner_l ) . ');}}'; // Set the CSS inline wp_add_inline_style( 'theme-css', $page_banner_css ); } } add_action( 'wp_enqueue_scripts', 'the_theme_css' );

…and as it appears in <head>:

<style id="theme-css-inline-css"> .banner{background-image:url(https://example.com/path/to/banner-500x500.jpg);}@media(min-width:43.75em){.banner{background-image:url(https://example.com/path/to/banner-1600x600.jpg);}} </style>

Using the images in a theme

The following markup uses the .banner class to set the background image:

<style> .banner { background-position: center center; background-size: cover; } .banner__inner { display: flex; flex-direction: column; justify-content: center; } </style> <div class="banner"> <div class="banner__inner"> <h1>The Bee</h1> <p>Studies have shown dramatic declines in bee colonies.</p> </div> </div>

… and achieves the following output:

Existing images

Existing images are not automatically resized when new image dimensions are specified via add_image_size(). WordPress needs to be instructed to regenerate this media.

If you're using WP-CLI, it's just a case of running 'wp media regenerate'. All new image size definitions will become available as a result. To generate from a single image you can add the image id to the end of that command: 'wp media regenerate 10' would only apply to the uploaded image with an ID of 10.

If WP-CLI isn't an option, there are a number of plugins devoted to this task.

Choosing breakpoints for images

Natural breakpoints for textual content will usually be suggested by the content itself: when the layout begins to look unintentional, a new breakpoint needs to be set. However, choosing image breakpoints can require a bit more thought. In this case it's wise to consider both the file size and whether the image remains visually meaningful (a bad crop or strange alignment will turn a 'content' image into a 'texture' image).