Hero Images Without Compromise: Fast, Clear, and Adaptive
Optimizing hero images is key for web performance and user experience. This article explores responsive image strategies—srcset, multiple images, and <picture>. And highlights best practices for fast loading, clarity, and bandwidth efficiency across devices.
Challenges of Responsive Images
For performance, SEO, and user experience, pages should load as quickly as possible. Images are often the largest bandwidth consumers. We want images to be as small as possible and only download files that match the current screen width and pixel density. However, on responsive pages, when image aspect ratios change across breakpoints, images can appear blurry or unnecessarily large. This is especially common with hero images. We'll use it as an example to introduce possible solutions.
Using responsive images in HTML
A common approach is to use an <img> element with sizes and srcset:
<img
srcset="https://example.com/hero-480.jpg 480w,
https://example.com/hero-1600.jpg 1600w"
sizes="100vw"
src="<https://example.com/hero.jpg>"
alt="hero"
/>
srcset defines a set of candidate images for the browser to choose from, each with a different intrinsic width. sizes specifies media conditions and tells the browser which width from srcset is expected under each condition.
With Next.js we can use the Image component and a loader to generate srcset and sizes automatically:
import Image from "next/image";
const imageLoader = ({ src, width, quality }) => {
return `https://example.com/${src}?w=${width}&q=${quality || 75}`;
};
export default function Hero() {
return (
<Image
fill
loader={imageLoader}
src="hero.jpg"
sizes="100vw"
alt="hero"
/>
);
}
Here’s how the hero image looks in desktop and in mobile:
)
)
Form the images we can see the problem: the required aspect ratio often depends on the screen size. On large displays the image aspect ratio is often larger, while on small devices the same ratio usually looks awkward. If we increase the height, the image gets stretched and then cropped, and becomes blurry. Or we must download a much larger image in mobile.
Using Multiple image elements with display toggles
We can render separate mobile and desktop images and toggle visibility with CSS classes.
<Image
fill
loader={imageLoader}
src="mobile-hero.jpg"
sizes="100vw"
className="md:hidden"
alt="Hero mobile"
/>
<Image
fill
loader={imageLoader}
src="desktop-hero.jpg"
sizes="100vw"
className="max-md:hidden"
alt="Hero desktop"
/>
It works! On mobile, it displays mobile-hero and the image remains clear, on desktop, it shows desktop-hero. When an Image component is hidden, its image is not downloaded, which saves bandwide.
)
)
But there is still a downside: As a hero, the First Contentful Paint (FCP) image shouldn’t be lazy-loaded but should have priority, otherwise it starts loading only after layout is calculated, which is too late. If you disable lazy-loading on both images, both may download, wasting bandwidth.
A better solution: with multiple elements
Use <picture> and <source> with media queries. The browser will only download the matching source for the current viewport.
<picture>
<source
srcset="/images/desktop-hero-1600.jpg 1600w,
/images/desktop-hero-1200.jpg 1200w"
media="(min-width: 768px)"
sizes="(min-width: 1440px) 1600px, 100vw"
/>
<source
srcset="/images/mobile-hero-800.jpg 800w,
/images/mobile-hero-480.jpg 480w"
media="(max-width: 767px)"
sizes="100vw"
/>
<img
src="fallback-image.jpg"
sizes="100vw"
alt="Hero image"
decoding="async"
fetchpriority="high"
/>
</picture>
In every <source> we can also use sizes to guide which resource is downloaded. The <img> element is the fallback and should point to a reasonable default. For the main hero image we shouldn’t forget setting fetchpriority to "high" to improve FCP.
In Next.js instead of <img> we can also use <Image> inside for optimization, the <source> elements remain the same
Summary
Among the three approaches above, when the aspect ratio varies significantly across breakpoints, I tend to prefer using <picture>. Although the developer experience may be slightly less convenient than using the prebuilt component like nextjs Image component, the user experience is better: only the necessary data is downloaded, and image clarity is preserved.
In the age of AI, assembling a page is easy, but creating a smooth, resource-efficient website comes down to attention to detail—even for a “simple” hero image. That’s why handing projects over to experienced professionals often delivers the best results.
)
)
)
)
)
)
)
)
)