CSS image() and image-set() for Responsive Image Values
CSS image functions let you define image values directly in styles, including responsive image choices for different screen densities. They are especially useful for backgrounds and other CSS properties that accept images, where you want the browser to pick the best asset without extra scripting.
Quick answer: Use image-set() when you want the browser to choose between image files such as 1x and 2x versions. Use image() when you want a CSS image value with optional fallback behavior and, in some browsers, more advanced source handling.
Difficulty: Intermediate
You'll understand this better if you know: basic CSS syntax, how background-image works, and the idea of device pixel ratios like 1x and 2x.
1. What Are image() and image-set()?
image() and image-set() are CSS image values. They are not HTML elements and not JavaScript APIs; instead, they are functions you can place inside properties that accept an image, such as background-image, border-image-source, or content in some contexts.
- image-set() lets the browser choose from multiple versions of the same image based on pixel density or related selection rules.
- image() is a CSS image function that can wrap a source and provide fallback behavior in supported browsers.
- Both are used to express image choices in CSS rather than in markup.
- They are most often used for background images and decorative assets.
These functions matter because CSS can then select the most appropriate file for the device, reducing blur on high-resolution screens and limiting unnecessary bandwidth on lower-resolution screens.
2. Why CSS Image Functions Matter
Without responsive image values in CSS, you often need one image file for every case or a single oversized file that looks sharp everywhere but wastes bandwidth. CSS image functions help close that gap for style-driven images.
They are useful when the image is part of presentation rather than content, such as a hero background, a decorative card texture, or a logo variant in a stylesheet.
They also keep image selection close to the style rule that uses the image, which makes maintenance easier in systems with multiple themes, breakpoints, or density targets.
3. Basic Syntax or Core Idea
The most common function in practice is image-set(). It accepts several image candidates and lets the browser choose one. Each candidate usually includes a URL and a resolution descriptor.
Minimal image-set() syntax
The pattern is straightforward: list one or more image sources, then annotate each with a density such as 1x or 2x.
.hero {
background-image: image-set(
"hero-1x.jpg" 1x,
"hero-2x.jpg" 2x
);
}
In this example, the browser can choose the 2x file on dense displays and the 1x file on standard displays. The CSS stays readable and the image choice remains declarative.
Basic image() idea
The image() function is a CSS image value that can be used where the property accepts an image source. Support and exact behavior vary by browser, so it is less commonly used than image-set() in everyday projects.
.panel {
background-image: image("panel.png");
}
In practice, most developers rely on image-set() for multi-resolution assets and use normal url() values when they do not need selection logic.
4. Step-by-Step Examples
The examples below show how these functions work in common CSS situations. The goal is not just to memorize syntax, but to understand how the browser decides which image to use.
Example 1: A background with 1x and 2x versions
This is the classic use case for image-set(): the same visual asset in two resolutions.
.banner {
background-image: image-set(
"banner-small.jpg" 1x,
"banner-large.jpg" 2x
);
background-size: cover;
}
This lets the browser choose a sharper source on high-density screens while keeping lower-density screens efficient.
Example 2: Using image-set() with a CSS fallback
Some teams provide a standard url() first, then override it with image-set() for browsers that support it well.
.logo-area {
background-image: url("logo.png");
background-image: image-set(
url("logo.png") 1x,
url("[email protected]") 2x
);
}
The fallback line gives older browsers a basic image, while the second line provides density-aware selection where supported.
Example 3: Choosing between image densities for a card
A card background often needs to look clean at multiple screen densities without changing layout.
.card {
background-image: image-set(
"card-texture-1x.png" 1x,
"card-texture-2x.png" 2x,
"card-texture-3x.png" 3x
);
}
Here the browser can select from three assets, which is helpful when the image is important to the visual design.
Example 4: Combining image-set() with media-query-based art direction
image-set() handles density, but it does not replace art direction. If you need a different crop or composition on small screens, combine it with media queries.
.hero {
background-image: image-set(
"hero-wide-1x.jpg" 1x,
"hero-wide-2x.jpg" 2x
);
}
@media (max-width: 600px) {
.hero {
background-image: image-set(
"hero-tall-1x.jpg" 1x,
"hero-tall-2x.jpg" 2x
);
}
}
This example shows the key distinction: density selection happens inside image-set(), while composition changes happen with media queries.
5. Practical Use Cases
CSS image functions are best for decorative and presentation-driven assets. Common project uses include:
- Background images for headers, hero sections, and cards.
- High-density logos used in banners, footers, or branded panels.
- Theme-specific art assets that need different versions for light and dark designs.
- Retina-ready textures, noise overlays, and decorative borders.
- Density-aware sprite-like visual assets managed from CSS rather than HTML.
If the image is content, such as a product photo or article illustration, HTML image markup usually gives you more control over accessibility and responsive behavior.
6. Common Mistakes
Most problems with these functions come from confusing density switching with layout switching, or from expecting every browser to support the same syntax exactly the same way.
Mistake 1: Using image-set() for art direction
image-set() chooses between similar assets for different pixel densities. It does not automatically crop, reposition, or redesign an image for different screen sizes.
Problem: This code assumes the browser will pick a more suitable composition for mobile, but all candidates describe the same general visual role rather than a layout-specific crop.
.hero {
background-image: image-set(
"hero-desktop.jpg" 1x,
"hero-mobile.jpg" 2x
);
}
Fix: Use media queries for different crops or compositions, and keep image-set() for density variations within each layout.
.hero {
background-image: image-set(
"hero-desktop-1x.jpg" 1x,
"hero-desktop-2x.jpg" 2x
);
}
@media (max-width: 600px) {
.hero {
background-image: image-set(
"hero-mobile-1x.jpg" 1x,
"hero-mobile-2x.jpg" 2x
);
}
}
The corrected version works because it separates size-based art direction from resolution-based selection.
Mistake 2: Forgetting the resolution descriptor
Every candidate in image-set() needs a usable descriptor in practice. If the browser cannot determine how to compare candidates, selection may fail or behave unexpectedly.
Problem: This example looks close to valid CSS, but the candidates are not annotated with resolution information, so the browser has no clear basis for choosing between them.
.tile {
background-image: image-set(
"tile.png",
"[email protected]"
);
}
Fix: Add explicit density descriptors such as 1x and 2x.
.tile {
background-image: image-set(
"tile.png" 1x,
"[email protected]" 2x
);
}
The corrected version works because the browser can compare density values directly.
Mistake 3: Expecting image() support everywhere
image() is not as universally used as url() or image-set(). If you depend on it without fallback planning, some browsers may ignore the declaration.
Problem: This code can appear to work in one browser and then fall back unexpectedly in another browser that does not support the function in the same way.
.frame {
background-image: image("frame.png");
}
Fix: Provide a standard image value first, then layer in the more advanced value where supported and needed.
.frame {
background-image: url("frame.png");
background-image: image-set(
url("frame.png") 1x,
url("[email protected]") 2x
);
}
The corrected version works because it preserves a broadly supported baseline while still offering responsive selection.
7. Best Practices
These practices help you avoid blurry assets, unnecessary downloads, and hard-to-debug browser differences.
Practice 1: Use image-set() for density variants of the same asset
Keep all candidates visually equivalent except for resolution. That makes the browser’s choice predictable and prevents layout surprises.
.badge {
background-image: image-set(
"badge.png" 1x,
"[email protected]" 2x
);
}
This pattern keeps the CSS simple and ensures the visual design remains consistent across screens.
Practice 2: Keep art direction outside image-set()
If the image should change composition, use media queries or container logic, then use image-set() inside each branch if needed.
@media (min-width: 900px) {
.cover {
background-image: image-set(
"cover-wide-1x.jpg" 1x,
"cover-wide-2x.jpg" 2x
);
}
}
This keeps the purpose of each CSS feature clear and easier to maintain.
Practice 3: Always test on a standard screen and a high-DPI screen
Some issues only appear when the device pixel ratio changes. Test both 1x and 2x environments so you know the selected asset is crisp and appropriately sized.
.thumbnail {
background-image: image-set(
"thumb-1x.jpg" 1x,
"thumb-2x.jpg" 2x
);
}
Testing on different displays confirms that the browser is choosing the file you expected.
8. Limitations and Edge Cases
CSS image functions are useful, but they have important limits.
- image-set() is for choosing between related image resources, not for changing the meaning of the image.
- Browser support for image() can be more uneven than for url() or image-set().
- These functions do not replace HTML responsive image features such as source selection for content images.
- If your image is purely decorative, CSS is a good fit; if it conveys content, consider HTML so alternative text and semantics are available.
- Very old browsers may ignore advanced image syntax and fall back to the next supported declaration.
A common “not working” report is that the 2x file never appears. That often happens because the device pixel ratio, the browser’s zoom level, or the asset descriptors do not make the 2x candidate the best choice.
9. Practical Mini Project
Let’s build a small card component that uses image-set() for a decorative header background and a sensible fallback image for broader support.
.feature-card {
padding: 1.5rem;
border-radius: 1rem;
color: #fff;
background-color: #1f2937;
background-image: url("card-bg-1x.jpg");
background-image: image-set(
url("card-bg-1x.jpg") 1x,
url("card-bg-2x.jpg") 2x
);
background-size: cover;
background-position: center;
}
.feature-card h3 {
margin-top: 0;
}
This small pattern is common in production: one reliable fallback declaration, then a responsive image value that improves rendering on high-density screens.
10. Key Points
- image-set() lets CSS choose among multiple image files, usually by screen density.
- image() is a CSS image function with more limited everyday use and browser support considerations.
- These functions are ideal for decorative or background images, not for semantic content images.
- Use media queries for art direction and image-set() for resolution selection.
- Always plan a fallback for browsers that do not support the advanced syntax you use.
11. Practice Exercise
- Create a hero section that uses one background image on standard screens and a sharper version on high-DPI screens.
- Add a second rule for small screens that swaps in a different crop, while still providing 1x and 2x assets.
- Make sure the design still looks acceptable if the advanced image function is ignored.
Expected output: A hero background that looks crisp on retina displays and uses a different composition on narrow screens.
Hint: Use media queries for the crop change, and image-set() inside each layout branch for density switching.
.hero {
min-height: 20rem;
background-size: cover;
background-position: center;
background-image: url("hero-desktop-1x.jpg");
background-image: image-set(
url("hero-desktop-1x.jpg") 1x,
url("hero-desktop-2x.jpg") 2x
);
}
@media (max-width: 600px) {
.hero {
background-position: top center;
background-image: image-set(
url("hero-mobile-1x.jpg") 1x,
url("hero-mobile-2x.jpg") 2x
);
}
}
12. Final Summary
image-set() gives CSS a clean way to choose between multiple versions of the same image, which is most useful for backgrounds and other decorative assets. It helps you serve a sharper file on dense displays without forcing every user to download the largest version.
image() is part of the same family of CSS image values, but it is less central in everyday responsive workflows. In practice, most developers use url() for simple cases and image-set() when they need density-aware selection.
For the best results, separate density switching from art direction, keep a fallback in mind, and test on more than one display type. If you want to go further, the next useful topic is how CSS background images interact with background-size, background-position, and media queries.