How to use hotspot and crop attributes for images in Sanity queries?

36 replies
Last updated: Jan 29, 2026
Hi, I just started with sanity not long ago. I was wondering how I can modify my image from the query using the hotspot and crop attributes from the query. I went through the documentation but it seems that it didn’t work for me.
AI Update

I understand the confusion with hotspot and crop data! The key thing to know is that when you query an image with hotspot/crop data, that data is automatically included in the image object, but how you use it depends on which tool you're working with.

Let me break down the most common scenarios:

Using @sanity/image-url (Image URL Builder)

When you use the image URL builder, the hotspot and crop data are automatically applied when you pass the entire image object to it:

import imageUrlBuilder from '@sanity/image-url'

const builder = imageUrlBuilder(sanityClient)

function urlFor(source) {
  return builder.image(source) // Pass the whole image object
}

// In your GROQ query, just get the image field normally:
// *[_type == "post"][0].mainImage

const imageUrl = urlFor(image)
  .width(400)
  .height(300)
  .url()

The builder will automatically read the hotspot and crop properties from your image object and apply them to the transformations.

Common Issue: Not Passing the Complete Image Object

The most common mistake is only passing the asset reference instead of the entire image object. Make sure your GROQ query includes the full image field:

// âś… Correct - gets the whole image object including hotspot/crop
*[_type == "post"][0]{
  mainImage
}

// ❌ Wrong - only gets the asset reference
*[_type == "post"][0]{
  "imageUrl": mainImage.asset->url
}

Manual Implementation with CSS

If you're using frameworks like Next.js where the hotspot doesn't seem to work automatically (a common issue), you can manually apply the hotspot using CSS:

function getPositionFromHotspot(hotspot) {
  if (!hotspot || !hotspot.x || !hotspot.y) return "center";
  return `${hotspot.x * 100}% ${hotspot.y * 100}%`;
}

// Usage with Next.js Image
<Image
  src={urlForImage(image).url()}
  alt={image.alt}
  fill
  style={{
    objectFit: 'cover',
    objectPosition: getPositionFromHotspot(image?.hotspot)
  }}
/>

Enabling Hotspot in Your Schema

Make sure hotspot is enabled in your schema first:

{
  name: 'mainImage',
  type: 'image',
  options: {
    hotspot: true  // This enables the hotspot feature
  }
}

What the Hotspot Data Looks Like

When you query an image with hotspot, the data structure looks like this:

{
  _type: 'image',
  asset: { _ref: '...', _type: 'reference' },
  hotspot: {
    x: 0.5,      // 0-1 (percentage from left)
    y: 0.3,      // 0-1 (percentage from top)
    height: 0.4,
    width: 0.4
  },
  crop: {
    top: 0,
    bottom: 0,
    left: 0,
    right: 0
  }
}

The most likely issue you're facing is either not passing the complete image object to the URL builder, or working with a frontend framework that needs the manual CSS approach. If you're using the image URL builder correctly with the full image object, the hotspot should work automatically without any extra code!

Show original thread
36 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?