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
.
INFO
At the time of writing this article, I'm using:
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:
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.
// 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.
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 <></>;
};
NOTE
For this basic example I'll be sharing most props between both images, you'll probably want to specify props for desktop and mobile.
And finally we're going to use this new data in our template:
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!
<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 only one image request (desktop version) is made.
INFO
To easily confirm the image switch you can set 2 very distinct images for src
and desktopSrc
That's it, hope you liked it!