Skip to content




The best Next.js & Sanity Image component

The best Next.js & Sanity Image component

Think you know Sanity images in Next.js? Our guide sets the record straight, with code snippets for easy implementation. Get the definitive approach here.


We'll keep it short and sweet.

This is the ideal Next Sanity Image. You may not like it, but this is what peak image performance looks like.

jsx
import { ReactElement } from 'react';
import Image from 'next/image';
import { getImageDimensions } from '@sanity/asset-utils';
import { urlFor } from '~/lib/sanity';

export const IdealImage = ({ image }): ReactElement => {
  const alt = image?.alt ?? "image broke";
  const dimensions = getImageDimensions(image); 
  return (
    <div>
      ...
      {image?.asset && (
        <Image
          src={urlFor(image).url()}
          alt={alt}
          width={dimensions.width}
          height={dimensions.height}
          placeholder="blur"
          blurDataURL={urlFor(image).width(24).height(24).blur(10).url()}
          sizes="
            (max-width: 768px) 100vw,
            (max-width: 1200px) 50vw,
            40vw"
        />
      )}
      ...
    </div>
  );
};

So let's start by breaking down what each bit does, why it's useful, and why you're not going to break your entire website by trying to reference an image that doesn't exist within the preview.

getImageDimensions

I'd put a fairly hefty bet on the fact you haven't seen this before. It's an absolute banger of a utility library that allows you to pull dimensions from an image.

Why is that useful? Well because one of the main features of using Next/Image is to avoid something called Cumulative Layout Shift (opens in new tab). In short, that's the thing when you go on a website and it shifts and you end up clicking on a crappy ad instead of what you wanted.

blurDataURL

You've all seen those lovely blur up effects on Next/Image, so we thought we'd build something identical using the @sanity/image-url (opens in new tab). This essentially loads a much smaller version first and with added blur, makes it look elegant. Then moves onto the higher quality version when loaded

Sizes="..."

This is pretty cool if you haven't seen this before. This is Next.js way of handling the different sizes for the different screen sizes. Essentially this is saying:

(max-width: 768px) 100vw

Up to a size of 768px, the maximum size will be 100% width of the screen. This means that when you're on a mobile or small tablet. The maximum size rendered will be the full width of that screen (as you mostly stretch images to the size of the screen in responsive views).

See below for how it looks on mobile - can you see how the image is nearly 100% of the width of the screen?

Image on mobile screens

(max-width: 1200px) 50vw

This on the other hand is saying that below 1200px, the image will be a maximum of 50vw e.g. if the screen is 1000px wide, at most the image will be 500px.

The reason for the difference in change is that instead of the image being 100% width, we now stack the image on the right, and the text on the left.

Take a look at this now, it makes sense right?

Image on the right, text on the left showing the screen on large tablet, small desktop mode

40vw

So everything above 1200px is now going to be at max 40vw. That's the easiest one to get. The reason for doing so is that the container progressively takes up less and less of the screen real estate and is going to probably be less than 40% of the overall size.

Again, pretty clever, and works like a charm

Services
$ Sanity CMS development
Image optimization, CDN setup, and Sanity + Next.js performance tuning.
>Learn more

The image and text showing how it takes up less than 40% of the screen real estate above 1200px

No spam, only good stuff

Subscribe, for more hot takes

Only god knows why anybody would purposefully subscribe themselves to a newsletter that moans about development. These poor souls did though
Profile 1
Profile 2
Profile 3
Profile 4
Profile 5

Aspect Ratio & Cumulative Layout Shift (CLS)

Aspect ratio (the proportional relationship between an image's width and height) is an essential aspect of web page design. Google highly values the optimal use of aspect ratio and static heights & widths, according to Cumulative Layout Shift (CLS) – a metric measuring the surprise positional changes of contents during page load. Heavy CLS often distorts user experience as it results in accidental clicks or difficulty in reading. If you want to actually see exactly how good/bad your current CLS there's a great tool here (opens in new tab)

Image Rendering Modes

There are typically four common ways to handle image displaying, especially considering responsiveness:

1. width: auto and height: auto: Both preserve the original aspect ratio within the parent container's dimensions. However, they may not cover its full extent.

2. width: 100% and height: auto: Forces the image to span the full width of its parent, self-adjusting its height to retain the original proportion.

3. objectFit: "cover": Corresponding CSS's background-size: cover, it scales the image to cover its parent, potentially cropping parts due to aspect ratio differences.

4. Static width and height values: Ignores the parent size and original aspect ratio, rigorously sets the image's height and width.

Any other parts?

Well, that's about it, it's using the latest version of Next/Image so be aware that there have been significant changes over the years, but as for the latest and greatest, this is what Roboto (opens in new tab) is using. If you want us to set this up for you as part of a wider Sanity and Next.js build, give us a shout.

Frequently asked questions

What is the best way to render Sanity images in Next.js?
Use next/image with the src from urlFor(image).url(), width and height pulled from getImageDimensions, placeholder set to blur with a tiny blurred version as blurDataURL, and a sizes attribute that describes how wide the image renders at each breakpoint. Wrap it in a check for image.asset so a missing image does not break the page in preview.
What does getImageDimensions do?
It is a utility from @sanity/asset-utils that pulls the width and height out of a Sanity image reference. Passing those to next/image reserves the right space before the image loads, which avoids Cumulative Layout Shift, the thing where a page jumps and you click an ad instead of what you wanted.
How do I get a blur-up placeholder for Sanity images in Next.js?
Set placeholder to blur and generate blurDataURL with the Sanity image URL builder: urlFor(image).width(24).height(24).blur(10).url(). That loads a much smaller blurred version first, then swaps in the full quality image when it is ready.
What does the sizes attribute do on next/image?
It tells Next.js how wide the image will render at different screen widths so it can serve an appropriately sized file. Our example: 100vw up to 768px (full width on mobile), 50vw up to 1200px (image stacked beside text), and 40vw above that, as the container takes up less of the screen.
Why does Cumulative Layout Shift matter for images?
CLS measures surprise positional changes during page load, and Google values keeping it low. Heavy CLS causes accidental clicks and makes pages hard to read. Giving images static widths and heights, or a fixed aspect ratio, prevents the shift. We link to a layout shift GIF generator tool for checking your own pages.
What are the common image rendering modes for responsive layouts?
Four common ones: width and height both auto (keeps the aspect ratio, may not fill the container); width 100% with height auto (fills the width, height follows); objectFit cover (scales to cover the parent, cropping if needed, like background-size: cover); and static width and height values that ignore both the parent and the aspect ratio.

About the author

Jono Alford

Founder of Roboto Studio, specializing in headless CMS implementations with Sanity and Next.js. A Sanity Pioneer and first-cohort Sanity Community Ambassador, focused on editorial experiences that help teams ship faster.



Related posts





Get in touch

Tell us what you're building. We reply within one working day. Jono or someone on the team picks up every message personally.

By sending this you agree to our privacy policy. We only use your details to reply.