How to Use the HTML Picture Element with Next.js?
- article

Introduction
This guide will show you how to use the HTML <picture>
element with a Next.js app. It will also go into detail on how to optimize performance, using advanced preloading techniques.
The HTML picture element allows developers to display different images for different devices or screen sizes. It enables art direction, where you serve entirely different images depending on the context, not just resized versions of the same image.
This is especially useful when different crops or compositions are better suited to different screen sizes — for example, a wide landscape on desktop and a centered square on mobile.
<picture>
<source media="(min-width: 1024px)" srcset="/images/hero-desktop.jpg" />
<source media="(min-width: 431px)" srcset="/images/hero-tablet.jpg" />
<img src="/images/hero-mobile.jpg" alt="Scenic view of mountains and river" />
</picture>
Next.js Image Component
The built-in Next.js Image component doesn't support the <picture>
element. It's optimized for responsive image sizing via srcSet
and sizes
, but it doesn't allow for art direction — you can't define separate images for different breakpoints or aspect ratios.
With the release of v14.1.0, Next.js added a new function called getImageProps( )
This function exposes some of the under the hood Image API and allows you to get props to pass to an HTML <img>
element.
While this can be used to generate the markup needed to support <picture>
, preloading of images is not supported.
The typical way to do this with the Image component or getImageProps()
with a <img>
element, is setting priority
to true
.
import { getImageProps } from "next/image";
const { props: desktopProps, ...desktopPropsRest } = getImageProps({
width: 1440,
height: 875,
quality: 80,
src: "https://picsum.photos/id/502/1440/875",
// Has no effect, and does not add preload links to `<head>`.
priority: true,
});
<picture>
<source
media="(min-width: 1000px)"
{...desktopPropsRest}
srcSet={desktopProps.srcSet}
/>
</picture>;
However, this has no effect when using getImageProps()
with the HTML <source>
element, and does not add <link rel="preload" as="image">
tag to the document's <head>
. It seems
under the hood of the Next.js Image API, priority
prop only triggers adding preload links to the <head>
if it's used in conjunction with the <img>
element.
This can present a performance hurdle if you are using responsive images above the fold, and they are flagged as the Largest Contentful Paint (LCP)
Next Image Plus
next-image-plus is a set of primitive React components, built on top of the Next.js Image API and React 19, which adds full support for picture and background images, including preloading any <picture>
elements flagged as the Largest Contentful Paint (LCP).
Install the package
First we need to install next-image-plus package from npm:
npm install next-image-plus
Using the Picture, Source, and Img components
next-image-plus provides 3 components for creating responsive images with art direction:
<Picture />
component provides a wrapper for zero or more Source components and one Img component.<Source />
component specifies an image and a media query.<Img />
component provides a fallback image
To set up the <Picture />
component, we'll need to import the following inside our Next.js component:
import { Picture, Source, Img } from "next-image-plus";
The HTML <picture>
element allows for multiple source images, for our demo here, we're going to use 3 different sized images: medium, large, and a fallback.
This table maps out our different breakpoints, images, and media queries we'll use:
Breakpoint | Image | Media | Width | Height |
---|---|---|---|---|
medium | https://picsum.photos/id/59/860/430 | (min-width: 430px) and (max-width: 1024px) | 860px | 430px |
large | https://picsum.photos/id/59/220/220 | (min-width: 1024px) | 220px | 220px |
fallback | https://picsum.photos/id/59/430/215 | (max-width: 430px) | 430px | 215px |
Using the components, we can create a picture
element with 2 sources and a fallback image.
<Picture>
{/* Medium breakpoint */}
<Source
media="(min-width: 430px) and (max-width: 1024px)"
src="https://picsum.photos/id/59/860/430"
width={860}
height={430}
/>
{/* Large breakpoint */}
<Source
media="(min-width: 1024px)"
src="https://picsum.photos/id/59/220/220"
width={220}
height={220}
/>
{/* Fallback */}
<Img
src="https://picsum.photos/id/59/430/215"
width={430}
height={215}
alt="Mountains and a river"
/>
</Picture>
In the browser, this will render the following HTML:
<picture>
<source
media="(min-width: 431px) and (max-width: 1023px)"
width="860"
height="430"
srcset="
/_next/image?url=https://picsum.photos/id/59/860/430&w=860&q=75 1x,
/_next/image?url=https://picsum.photos/id/59/860/430&w=1920&q=75 2x
"
/>
<source
media="(min-width: 1024px)"
width="220"
height="220"
srcset="
/_next/image?url=https://picsum.photos/id/59/220/220&w=256&q=75 1x,
/_next/image?url=https://picsum.photos/id/59/220/220&w=640&q=75 2x
"
/>
<img
src="/_next/image?url=https://picsum.photos/id/59/430/215&w=860&q=75"
width="430"
height="215"
alt="Mountains and a river"
/>
</picture>
A key difference between the native html element for <source>
and the <Source>
component, is the html element does not accept a src
attribute, but uses srcset
instead.
The value for srcset
will get automatically generated for you, using the Next.js Image API, based on the src
prop value.
Preloading for LCP
If your image is flagged as the Largest Contentful Paint (LCP), preloading can speed up performance and improve your Core Web Vitals score.
The <Picture />
component has built-in support for preloading images via the preload prop:
<Picture preload={true}>
When preload
is set to true, the component will add a <link rel="preload" as="image">
tag to the document's <head>
. In the browser, this will render the following HTML:
<head>
<link
rel="preload"
as="image"
fetchpriority="high"
imagesrcset="/_next/image?url=https://picsum.photos/id/59/430/215&w=430&q=75 1x, /_next/image?url=https://picsum.photos/id/59/430/215&w=860&q=75 2x"
media="(max-width: 430px)"
/>
<link
rel="preload"
as="image"
fetchpriority="high"
imagesrcset="/_next/image?url=https://picsum.photos/id/59/860/430&w=860&q=75 1x, /_next/image?url=https://picsum.photos/id/59/860/430&w=1920&q=75 2x"
media="(min-width: 431px) and (max-width: 1023px)"
/>
<link
rel="preload"
as="image"
fetchpriority="high"
imagesrcset="/_next/image?url=https://picsum.photos/id/59/220/220&w=256&q=75 1x, /_next/image?url=https://picsum.photos/id/59/220/220&w=640&q=75 2x"
media="(min-width: 1024px)"
/>
</head>
This tells the browser what images are critical resources, and which should be fetched as soon as possible. You can read more about preloading responsive images here
Conclusion
While the examples here are introductory, I hope it demonstrates the power of the next-image-plus <Picture />
component.
To see a more advanced example, you can check out the examples page
To learn more about the <Picture />
component, you can check out the API documentation
And if you find this package useful, please give us a star on GitHub