imageUrlBuilder not respecting hotspot and crop values from Sanity Studio

3 replies
Last updated: Jun 11, 2024
Tough time with the imageUrlBuilder (@sanity/image-url). It works fine to retrieve the image and manipulate it (set width/height flipping it etc...) but does not respect the
hotspot
and
crop
values I set in the studio.I've seen that the way the imageUrlBuilder works it by passing a SanityImageSource, which simply has a reference to the image document stored in the studio. How is it supposed to retrieve hotspot and crop values?
AI Update

The issue you're experiencing is a common one! The @sanity/image-url builder does automatically respect hotspot and crop values, but you need to pass the full image object to it, not just the asset reference.

The Key: Pass the Entire Image Field

When you query your data, make sure you're passing the complete image field (which includes asset, crop, and hotspot) to the image builder:

// ❌ This won't work - only has asset reference
const imageUrl = urlFor(document.image.asset).url()

// ✅ This works - includes crop and hotspot data
const imageUrl = urlFor(document.image).url()

Your GROQ Query Needs All the Data

Make sure your GROQ query fetches the complete image object with crop and hotspot. According to the Sanity documentation on displaying images, your query should look like this:

*[_type == "person"] {
  name,
  image {
    asset->,
    crop,
    hotspot
  }
}

The builder expects this structure:

{
  asset: { _ref: "image-abc123...", _type: "reference" },
  crop: { top: 0, bottom: 0, left: 0, right: 0 },
  hotspot: { x: 0.5, y: 0.5, height: 0.3, width: 0.3 }
}

How the Image Builder Handles Hotspot/Crop

When you pass the complete image object to @sanity/image-url, the builder automatically includes the crop and hotspot data in the URL it generates. This data tells Sanity's image CDN how to intelligently crop and focus the image based on the focal point you set in the Studio.

The hotspot contains x and y coordinates (as values between 0 and 1) that represent the focal point as a percentage of the image dimensions. When the image is transformed, Sanity's CDN ensures this focal point remains visible.

Troubleshooting Steps

  1. Verify your schema - Make sure hotspot: true is enabled in your image field schema:

    {
      name: 'image',
      type: 'image',
      options: {
        hotspot: true
      }
    }
  2. Check your query - Log the image object to confirm it has crop and hotspot fields with actual values

  3. Pass the full object - Pass image, not image.asset to the builder

  4. Test with transformations - Try adding width/height parameters to see the hotspot in action:

    urlFor(image).width(800).height(600).url()

Alternative: Manual CSS Implementation

If you're still having issues with certain frontend frameworks (particularly Next.js), you can manually implement the hotspot using CSS object-position:

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

<img
  src={urlFor(image).url()}
  style={{ objectPosition: getPositionFromHotspot(image?.hotspot) }}
/>

The key takeaway is that @sanity/image-url automatically respects hotspot and crop when you pass the complete image object—you don't need to do anything special beyond ensuring your query includes all the necessary fields.

Show original thread
3 replies
I figured this out, for anyone wondering, I was passing the asset data contained in the image directly in the imageUrlFor() function. This will load the image, but won't use the crop/hotspot. Feeding the whole image object, conatining the asset, hotspot and crop data to imageUrlFor() instead of solely the asset will fix this.
Thanks for sharing your solution!
user K
So you're not using &lt;SanityImage&gt;?

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?