
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeThe 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.
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()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 }
}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.
Verify your schema - Make sure hotspot: true is enabled in your image field schema:
{
name: 'image',
type: 'image',
options: {
hotspot: true
}
}Check your query - Log the image object to confirm it has crop and hotspot fields with actual values
Pass the full object - Pass image, not image.asset to the builder
Test with transformations - Try adding width/height parameters to see the hotspot in action:
urlFor(image).width(800).height(600).url()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.
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store