Using Sanity Hotspot with GatsbyImage for Image Cropping

16 replies
Last updated: May 24, 2021
Hey everyone! Is it possible to use the Sanity hotspot data with a
GatsbyImage
? Maybe using the hotspot values with css
object-position
? I couldn’t find much documentation on what the values actually mean, e.g:
height: 0.43673837232083357
width: 0.33333333333333326
x: 0.4060283687943266
y: 0.4828572548097652
My use case is that I’m cropping photos into squares when they’re in a list but also shown full size elsewhere. I’d like to be able to choose the area that gets shown when in the cropped square with a Sanity hotspot. Thanks a lot for any help!
AI Update

Great question! Yes, you can absolutely use Sanity's hotspot data with GatsbyImage using CSS object-position. Let me break down what those values mean and how to use them.

Understanding Hotspot Values

The hotspot values you're seeing are normalized coordinates (0 to 1) representing positions and dimensions as percentages:

  • x: Horizontal position of the hotspot center (0 = left edge, 1 = right edge, 0.5 = center)
  • y: Vertical position of the hotspot center (0 = top edge, 1 = bottom edge, 0.5 = center)
  • width: Width of the hotspot area as a fraction of the total image width
  • height: Height of the hotspot area as a fraction of the total image height

So in your example, x: 0.406 and y: 0.483 means the hotspot is positioned at roughly 40.6% from the left and 48.3% from the top of the image.

Converting to CSS object-position

Here's a helper function to convert Sanity hotspot data to CSS object-position:

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

Using with GatsbyImage

You can apply this to your GatsbyImage component using the style or imgStyle prop:

import { GatsbyImage, getImage } from "gatsby-plugin-image"

const MyComponent = ({ image }) => {
  const gatsbyImage = getImage(image)
  const objectPosition = getObjectPosition(image.hotspot)
  
  return (
    <GatsbyImage
      image={gatsbyImage}
      alt={image.alt}
      imgStyle={{
        objectFit: "cover",
        objectPosition: objectPosition
      }}
    />
  )
}

For your square crop use case, this will ensure that when the image is cropped to a square, the area you selected in Sanity Studio remains visible and centered.

Querying Hotspot Data

Make sure you're querying the hotspot data in your GraphQL query:

query {
  sanityYourType {
    image {
      hotspot {
        x
        y
        height
        width
      }
      asset {
        gatsbyImageData(width: 400, height: 400, fit: FILLMAX)
      }
    }
  }
}

This approach is mentioned in Sanity's documentation on using hotspot with GatsbyImage and is a reliable way to handle focal points when the image URL builder doesn't automatically apply them in your frontend framework.

also curious. Right now i am also using GatsbyImage with Sanity but dont know exactly if using
gatsby-plugin-sanity-image
has any advantage over plain GatsbyImage. Especially when having hotspot in mind
• Applies Sanity hotspot data as the 
object-position
 in case you need it
So it looks like that the plugin is the way to go.
That’s helpful thanks
user U
! Had a look at the plugin code and it’s just setting the hotspot values straight in as a percentage:

css={
  hotspot && {
    objectPosition: [hotspot.x, hotspot.y]
    .map((value) => (value * 100).toFixed(2) + "%")
    .join(" "),
  }
}
Will try to play around with it and a GatsbyImage now
gimme feedback how this goes. I really would like to stick with plain gatsby ImagePlugin
but bear in mind that sharp also has some fancy options for guessing how you want to crop based on skin tones.
This should work pretty well for portraits… not so for skyscrapers i assume 😉
Yeah it would be really cool to be able to do it automatically but unfortunately I’m working with artwork at the moment
It’s kind of working? As in moving the hotspot around in sanity does move the image crop accordingly - but it’s not in sync with the preview in sanity studio
This is with this GatsbyImage updating on the left:
<GatsbyImage
image={getImage(artwork.artworkImage.asset)}
alt={""}
style={{ width: "100%", height: "100%" }}
imgStyle={{
  width: `100%`,
  height: `100%`,
  objectPosition: `${hotspot.x * 100}% ${hotspot.y * 100}%`,
}}
/>
In Sanity Studio they aren’t using object-position on the previews though, they’re overflowing the preview elements with e.g:
position: absolute;
overflow: hidden;
height: 100%;
width: 138.07%;
top: 0px;
left: -12.91%;
ah thanks for feedback. Looks nice so far.
will do my tests most likely tomorrow.
mainly thinking about using the hotspot thing for social media sharing images as in og:image
Also did some tests. Same finding as you. But with hotspot i can replace my previous way of doing things. Before i used ArtDirection and delivered different images based on different breakpoints (screen sizes). Hotspot kind of replaces this optically… with the drawback that the image size wont be smaller with smaller screens. But will switch anyway. Maintaining different header image sizes depending on sreen size is too much effort.
One thing i noticed though: The sanity image plugin is also able to make usage of the crop feature… something the gatsby image plugin cant

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?