Sanity image focal point not working with @sanity/image-url in NextJS
The issue you're experiencing is that the hotspot won't be respected unless you specify both width and height dimensions. The @sanity/image-url library needs explicit dimensions to know how to apply the hotspot/crop data.
Here's what's happening: when you call urlFor(args.data.cover).url() without dimensions, the library returns the original image URL without any transformations. The hotspot data exists in your image object, but it requires width and height parameters to be activated.
Solution: Add width and height
Update your urlFor function or the specific call to include dimensions:
function urlFor(image) {
const builder = imageUrlBuilder(sanity);
return builder.image(image);
}
// In your JSX - specify dimensions where you use it
<img
alt={args.data.metadata.alt}
src={urlFor(args.data.cover).width(800).height(600).url()}
className="w-full h-full object-cover"
/>When you provide both width and height, @sanity/image-url automatically converts the hotspot data from your image document into the proper URL parameters (crop=focalpoint, fp-x, and fp-y) as documented in Sanity's image URL transformation guide.
For responsive layouts with Tailwind
Since you're using w-full h-full with Tailwind, you likely want the image to fill its container while respecting the hotspot. Here's a better approach:
<div id="image" className="w-full flex-1 relative">
<img
alt={args.data.metadata.alt}
src={urlFor(args.data.cover).width(1200).height(800).url()}
className="w-full h-full object-cover"
/>
</div>The object-cover class ensures the image fills the container while maintaining aspect ratio, and the hotspot will be respected in how the image is cropped by Sanity's CDN.
With next/image
If you do want to use Next.js's Image component (which is generally recommended for performance), the same principle applies:
import Image from 'next/image'
<div id="image" className="w-full flex-1 relative">
<Image
src={urlFor(args.data.cover).width(1200).height(800).url()}
alt={args.data.metadata.alt}
fill
className="object-cover"
/>
</div>Why crop works but hotspot doesn't
The crop parameter you mentioned works because it's a simpler transformation that just defines rectangular boundaries. The hotspot, however, specifically requires dimensions to calculate where to focus when the image is resized to different aspect ratios. According to the @sanity/image-url documentation, the library states: "In order for hotspot/crop processing to be applied, the image record must be supplied, as well as both width and height."
The key takeaway: hotspot data is only applied when you specify both width and height, which tells Sanity's image pipeline how to intelligently crop around the focal point you've defined.
Show original thread13 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.