Best way to handle Sanity images with hotspots and cropping in Next.js?

11 replies
Last updated: Jan 29, 2026
what is the “best” way right now to do images in Next.js that supports hotspots, cropping etc? Particularly interested in a list page with images that are all the same size/proportion and deliver all the right browser layout hints. Any examples?
AI Update

For Next.js images with hotspot and cropping support, the best current approach is using @sanity/image-url with Next.js's next/image component. This combination gives you full control over Sanity's hotspot/crop features while delivering optimized images with proper browser hints.

Here's the modern approach for list pages with consistent aspect ratios:

1. Install and Configure @sanity/image-url

npm install @sanity/image-url

Create a reusable URL builder:

// lib/sanityImageUrl.ts
import { createImageUrlBuilder, type SanityImageSource } from '@sanity/image-url'
import { client } from './sanity/client'

const builder = createImageUrlBuilder(client)

export function urlFor(source: SanityImageSource) {
  return builder.image(source)
}

2. List Page Component with Consistent Sizing

For a grid/list where all images should be the same size and aspect ratio:

import Image from 'next/image'
import { urlFor } from '@/lib/sanityImageUrl'

export function ImageGrid({ items }) {
  return (
    <div className="grid grid-cols-3 gap-4">
      {items.map((item) => (
        <div key={item._id} className="relative aspect-square">
          <Image
            src={urlFor(item.image)
              .width(600)
              .height(600)
              .fit('crop')  // Ensures consistent aspect ratio
              .url()}
            alt={item.image.alt || ''}
            fill
            sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
            className="object-cover"
          />
        </div>
      ))}
    </div>
  }
}

Key Points for List Pages

The sizes attribute is crucial for proper browser hints. It tells the browser which image size to download based on viewport:

  • (max-width: 768px) 100vw = full width on mobile
  • (max-width: 1200px) 50vw = half width on tablet
  • 33vw = one-third width on desktop (for 3-column grid)

Hotspot preservation: The @sanity/image-url builder automatically respects hotspot and crop settings from your Sanity Studio. When you use .fit('crop'), it will intelligently crop around the hotspot area.

Enable Hotspot in Your Schema

{
  name: 'image',
  type: 'image',
  options: {
    hotspot: true  // Enables the hotspot/crop UI in Studio
  }
}

Alternative: Responsive Srcset for Different Sizes

For more advanced optimization with multiple breakpoints:

<Image
  src={urlFor(image).width(800).height(800).url()}
  srcSet={[
    `${urlFor(image).width(400).height(400).url()} 400w`,
    `${urlFor(image).width(800).height(800).url()} 800w`,
    `${urlFor(image).width(1200).height(1200).url()} 1200w`,
  ].join(', ')}
  sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
  alt={image.alt || ''}
/>

Why This Approach?

  1. Native Sanity integration: @sanity/image-url automatically handles hotspot/crop data
  2. CDN optimization: Leverages Sanity's globally-distributed image CDN
  3. Next.js benefits: Gets automatic lazy loading, blur placeholders, and responsive images
  4. Consistent aspect ratios: Using .width() and .height() with .fit('crop') ensures uniform sizing
  5. Proper browser hints: The sizes attribute enables efficient image selection

The combination of Sanity's image pipeline with Next.js's Image component gives you the best of both worlds - smart cropping that respects editorial decisions plus modern web performance optimizations.

Show original thread
11 replies

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?