Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
The CSS image-set()
function allows the browser to display the “best” or most optimal image from a given set of image files. We’ll see what the “best” image means in a bit, but in a nutshell, it takes images of different resolutions or formats and serves the browser the most fitting image based on the user’s device. It works anywhere <image>
is accepted, like for background images.
With image-set()
, we can serve the most optimal version of an image. You can think of it as yet another weapon in your responsive web design toolbox.
.element {
background-image: image-set(url("example.com/normal-image.png") 1x, url("example.com/big-image.png") 2x);
}
The image-set()
function is defined in the CSS Images Module Level 4 specification.
Syntax
<image-set()> = image-set( <image-set-option># )
<image-set-option> = [ <image> | <string> ] [ <resolution> || type(<string>) ]?
Arguments
The CSS image-set()
function takes one or more <image>
, except the image-set()
function itself (meaning you can’t use the function within the function). It accepts up to two optional arguments:
- A
type(<mime>)
to specify the image format (e.g.,type("image/jpeg")
) - A
<resolution>
(e.g.,1x
,2x
,300dpi
, etc.) to specify the image quality. If no resolution is defined, it defaults to1x
which is the image’s full natural size with no shrinking or zooming.
/* Type */
.banner {
background-image: image-set(
url("banner.AVIF") type("image/AVIF"),
url("banner.WebP") type("image/WebP"),
url("banner.jpeg") type("image/jpeg")
);
}
/* Resolution */
.hero {
background-image: image-set(
url("hero-low.jpeg") 1x, /* Standard resolution */
url("hero-high.jpeg") 2x, /* High DPI (Retina) */
url("hero-ultra.jpeg") 3x /* Ultra high DPI */
);
}
Basic usage
By providing more than one image, each with its own resolution, we can ensure every user gets the best image for their device, i.e., an image with the same resolution as their device’s display. This way, the browser isn’t downloading an overly heavy image file if the user is loading from a low-powered device. Same goes the other way around: users with high-powered devices can get the heavier version.
At the same time, when working with raster images, we want to load small files whenever network conditions aren’t ideal, such as dropping from a high-speed connection to a slower one. The image-set()
lets the browser choose the best option based on the device and network conditions.
.hero {
background-image: image-set(
url("image-res-1.png") 1x, /* Low resolution */
url("image-res-2.png") 2x, /* High resolution */
url("image-res-3.png") 3x /* Highest resolution */
);
}
This way, a standard laptop device will typically download the 1x
image, a higher-powered MacBook Pro with a retina display will likely pick the 2x
image, and some very high-density phones may even select a 3x
image. If the network is slow, the browser may decide to load a lower-resolution image instead.
In short, we’re giving the browser jurisdiction to make that decision. The browser knows a lot more about the user’s device and network connection than we do as developers, so it’s nice we have a “set it and forget it” sort of approach for accommodating users.
A 4K display, however, doesn’t necessarily mean a support for higher resolution. The browser chooses based on pixel density, so mobile and large-screen devices alike get the right, according to their screen capacities.
Multiple-format images
We can also let the browser pick the best format for each case, so we can deliver newer image formats while catering to old browsers. For a while, browsers have been using newer formats like AVIF and WebP since they have smaller file sizes, don’t lose as much quality as JPEGs and PNGs when compressed, and have better color depth handling, among others.
Also, the function’s type()
argument prevents the browser from downloading an unsupported format in the first place:
.hero {
background-image: image-set(
url("image.AVIF") type("image/AVIF"),
url("image.WebP") type("image/WebP"),
url("image.jpeg") type("image/jpeg")
);
}
With image-set()
, we can provide more than one format for the browser to select from, and even throw in legacy image formats (.tiff
anyone?) as a fallback for older browsers.
Fast-loading websites
Images are the largest contributor to a webpage’s weight, making them major culprits of poor performance. This has negatively impacted user experience for a long time, and it has even been recorded, by older studies, that 53% of mobile users abandon sites that take longer than three seconds to load.
Using the image-set()
function, we can optimize performance at least as far as which type of image is used in a specific instance, which can shave off precious bytes when downloading the page and contribute to a faster loading experience.
We can even combine images with different resolutions and different formats, such that the best resolution with the smallest file size is loaded first:
.hero-banner {
background-image: image-set(
/* Small, optimized images for slow connections */ url("hero-small.AVIF") 1x type("image/AVIF"),
url("hero-small.WebP") 1x type("image/WebP"),
url("hero-small.jpeg") 1x,
/* Higher quality for fast connections and high-DPI */ url("hero-large.AVIF") 2x type("image/AVIF"),
url("hero-large.WebP") 2x type("image/WebP"),
url("hero-large.jpeg") 2x
);
}
This way…
- a user on
3G
could get a50KB
AVIF instead of a2MB
JPEG, - a user on fiber internet with a Retina display gets the crisp, high-quality version, and
- everyone gets the best format based on what their browser supports.
image-set()
vs srcset
The CSS image-set()
function might look a lot like the HTML srcset
attribute at a glance. But the two are indeed different, as the following table outlines:
Feature | image-set() | srcset |
---|---|---|
Used in | CSS (background-image , content , etc.) | HTML <img> attribute |
Syntax | background-image: image-set("low.jpeg" 1x, "high.jpeg" 2x); | <img srcset="low.jpeg 1x, high.jpeg 2x"> |
Resolution support | Pixel density (1x , 2x , 3x ) | Pixel density (1x , 2x , 3x ) |
Supported formats | Supports different image formats in a single definition. | Does not include type() . Use <picture> for format negotiations. |
Width support | No width descriptors | Width descriptors (320w , 640w ). It requires sizes to work. |
Works with | Any property that takes images | The sizes attribute for viewport-based selection of <img> elements |
If you’re simply serving the best image resolution based on the user’s device and network connection, then using the HTML srcset
attribute may be your best bet. You can find a detailed explanation about the srcset
attribute with many examples of how to use it in our HTML Responsive Images Guide.
Browser support
The image-set()
function gained Baseline availability in 2023, so it works in every modern browser at the time of writing. However, you may still need the -webkit
prefix or the @supports
rule and a fallback to use them on older browsers.
.element {
/* Fallback */
background-image: url("test.png");
/* Chrome/Edge/Opera/Samsung, Safari will fallback to this as well */
background-image: -webkit-image-set(url("test.png") 1x, url("test-2x.png") 2x);
/* Standard use */
background-image: image-set("test.png" 1x, "test-2x.png" 2x);
}
Or:
/* Fallback */
background-image: url("fallback-image.jpeg");
@supports (background-image: image-set("test.jpeg" 1x)) {
background-image: image-set("image.jpeg" 1x, "image-2x.jpeg" 2x);
}