Last updated: June 11, 2025

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

  • article
William Luisi

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>

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 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).

First we need to install next-image-plus package from npm:

npm install next-image-plus

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:

BreakpointImageMediaWidthHeight
mediumhttps://picsum.photos/id/59/860/430(min-width: 430px) and (max-width: 1024px)860px430px
largehttps://picsum.photos/id/59/220/220(min-width: 1024px)220px220px
fallbackhttps://picsum.photos/id/59/430/215(max-width: 430px)430px215px

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>
Info

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.

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

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