imageUrlBuilder not respecting hotspot and crop values from Sanity Studio
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
Verify your schema - Make sure
hotspot: trueis enabled in your image field schema:{ name: 'image', type: 'image', options: { hotspot: true } }Check your query - Log the image object to confirm it has
cropandhotspotfields with actual valuesPass the full object - Pass
image, notimage.assetto the builderTest 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 thread3 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.