← All posts

Sep 16, 2025258 words4 min read

Using <picture> in Next.js for responsive images

A short guide on how to optimize images using the picture element in Next.js

Next.js thumbnail

Introduction

In this article I will guide you in how to use the picture element in Next.js.

If you tried using the picture element directly in Next.js, you probably already noticed your image has no optimizations. To fix it we'll be using a fairly new function from Next.js: getImageProps

Creating the ResponsiveImage component

On your components folder create a new component file, I'll be naming mine: ResponsiveImage.tsx.

Import getImageProps and create the base for the component:

ts
import { getImageProps } from 'next/image';

export const ResponsiveImage = () => {
return <></>;
};

Now I'm going to add some types for this component's prop, I'm going with a pretty basic type for brevity.

ts
// Import base image props type
import { getImageProps, type ImageProps } from 'next/image';

// We'll be using src as the source of truth for the image
type ResponsiveImageProps = ImageProps & {
desktopSrc: string;
};

export const ResponsiveImage = ({
// Extract src and desktopSrc from props spread
src,
desktopSrc,
...props
}: ResponsiveImageProps) => {
return <></>;
};

Moving forward, we'll now extract the props from the sources and apply common props.

ts
import { getImageProps, type ImageProps } from 'next/image';

type ResponsiveImageProps = ImageProps & {
desktopSrc: string;
};

export const ResponsiveImage = ({
src,
desktopSrc,
...props
}: ResponsiveImageProps) => {
const { props: mobileImageProps } = getImageProps({ src, ...props });
const { props: desktopImageProps } = getImageProps({
src: desktopSrc,
...props,
});

return <></>;
};

And finally we're going to use this new data in our template:

ts
import { getImageProps, type ImageProps } from 'next/image';

type ResponsiveImageProps = ImageProps & {
desktopSrc: string;
};

export const ResponsiveImage = ({
src,
desktopSrc,
...props
}: ResponsiveImageProps) => {
const { props: mobileImageProps } = getImageProps({ src, ...props });
const { props: desktopImageProps } = getImageProps({
src: desktopSrc,
...props,
});

return (
<picture>
<source
// Breakpoint at which point the image will switch
media="(min-width: 1280px)"
srcSet={desktopImageProps.srcSet}
/>

// Spread on your main image (I'm going mobile first, so I do it for the mobileImage)
<img {...mobileImageProps} />
</picture>
);
};

Usage is pretty straight forward, you just use it like the Image component!

ts
<ResponsiveImage
alt="Image alt"
src="/mobile-image.jpg" // Base image
desktopSrc="/desktop-image.jpg" // Desktop breakpoint image
fill // I'm using fill for simplicity
// Add other Image props as needed
/>

Testing it out

  • Open the page in a browser and access developer tools.
  • Navigate to the Network tab.
  • Reload the page and observe image requests:
    • You should see a single image request initially.
  • Resize the browser window:
    • Cross the 1280px breakpoint to trigger image switch.
    • Observe a new image request for the desktop version.
  • Reload the page while at desktop size:
    • Verify that only a single image request (desktop version) is made.