Discussion about scaling product images in Sanity with Next.js for an ecommerce platform, including suggestions for using the next-sanity-image library and enveloping the...

17 replies
Last updated: Jul 11, 2022
Hi guys, I'm new to sanity and just trying to get the hang of it introducing it with Nextjs for an ecommerce platform. I'm trying to se the main product image, but having an issue with the image scaling for the defined image size. Am I missing something?
AI Update

Welcome to Sanity! Image scaling issues with Next.js are pretty common when you're just getting started, so you're definitely not alone here. The good news is that there are some well-established patterns for handling Sanity images with Next.js.

The main thing you're likely running into is that you need to properly respect the hotspot and crop data that Sanity provides when you enable those features in your schema. Here's what you need to know:

Enable Hotspot in Your Schema

First, make sure you've enabled the hotspot feature in your image field definition:

{
  name: 'image',
  type: 'image',
  options: {
    hotspot: true
  }
}

This allows content editors to define a focal point that should remain visible when the image is cropped or resized - super important for product images where you want to keep the main product in view!

Using Images with Next.js

For Next.js projects, I'd recommend using the next-sanity-image package - it's specifically designed to bridge Sanity's image pipeline with the Next.js Image component and handles all the complexity around hotspot, crop, and responsive sizing.

Install it:

npm install next-sanity-image

Then use it like this:

import { createClient } from '@sanity/client';
import Image from 'next/image';
import { useNextSanityImage } from 'next-sanity-image';

const client = createClient({
  projectId: process.env.NEXT_PUBLIC_SANITY_PROJECT_ID,
  dataset: process.env.NEXT_PUBLIC_SANITY_DATASET,
  useCdn: true
});

const ProductImage = ({ product }) => {
  const imageProps = useNextSanityImage(client, product.image);

  return (
    <Image
      {...imageProps}
      style={{ width: '100%', height: 'auto' }}
      sizes="(max-width: 800px) 100vw, 800px"
      placeholder="blur"
      blurDataURL={product.image.asset.metadata.lqip}
    />
  );
};

Manual Approach with Image URL Builder

If you prefer more control, you can use @sanity/image-url directly:

import imageUrlBuilder from '@sanity/image-url';

const builder = imageUrlBuilder(client);

function urlFor(source) {
  return builder.image(source);
}

// Then use it:
const imageUrl = urlFor(product.image)
  .width(800)
  .height(600)
  .url();

The image URL builder automatically respects the hotspot and crop data when you pass in the full image object from your Sanity query.

Handling Hotspot with CSS

Sometimes the hotspot doesn't apply perfectly with certain Next.js layouts. In those cases, you can manually apply it using CSS object-position:

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

<Image
  src={urlFor(image).url()}
  alt={image.alt}
  fill
  style={{ 
    objectFit: 'cover', 
    objectPosition: getPositionFromHotspot(image.hotspot) 
  }}
/>

Don't Forget Next.js Config

Make sure your next.config.js allows loading images from Sanity's CDN:

module.exports = {
  images: {
    domains: ['cdn.sanity.io']
  }
};

Query the Right Data

When fetching your product data, make sure you're including the image metadata, hotspot, and crop information:

const query = `*[_type == "product"] {
  image {
    asset->{
      ...,
      metadata
    },
    hotspot,
    crop
  }
}`;

The next-sanity-image package handles most of the complexity for you and is the recommended approach for e-commerce sites where you need consistent, responsive product images. It automatically generates the right srcSet values and respects Sanity's image transformations, which is exactly what you need for an e-commerce platform.

Let me know if you're still running into issues after trying these approaches!

Does it make a difference if you append
.url()
to the end of your methods?

<img
  src={urlFor(productImg)
    .auto("format")
    .width(1000)
    .height(1000)
    .fit("fill")
    .url()}
  alt={productImg?.alt || `Photo of ${title}`}
/>
Thanks but no it doesn’t - I just want the image to scale to fit the container - but nothing I’ve tried works so far. It just always clips the image.
Thanks for confirming. Can you let me know what you get if you
console.log(productImg)
(before your component’s
return
of JSX)?
1. _{_key: ‘b7cf9331b38f’, type: ‘image’, asset: {…}} a. *asset*: {_ref: ‘image-1ecadba9d31e11105ff693d4c21f7ff1a5db33e0-1000x1333-png’, _type: ‘reference’}
b. *_key*: “b7cf9331b38f”
c. *_type*: “image”
d. [[Prototype]]: Object

Interesting. That seems good.
Can you try a smaller crop? maybe 300x300?
the image doesnt change size, just I can see the quality is reduced
just to be clear the image is going in a carousel
Oh good. That means the image being returned from
urlFor
is being modified. I expect the rest will be done with CSS.
required result is that the image scales to fit in the defined size
keeping aspect ratio
at the moment its cropping the image which isnt desired
What does your CSS look like? Depending on your constraints, I would expect you’d get close with a non-clipping fit method like
max
, and then CSS that’s something like (untested):

width: 100%;
max-width: 1000px;
display: block;
height: auto;

yeah im just taking a look at the carousel and I dont think its an issue with the image but something with the internal css in the carousel
Hey David, please use a single message to describe your problem so we can rely on a unique Slack thread. 😊
Ah ok thanks Kitty!
Ah yeah i had similar problems with scaling in nextJS.Sometimes your
Image
should be enveloped by a
div
with the specific css applied to it instead of the image component itself.Here you should use
next/image (if you don’t already use it of course 🙂 )
You might also try
this wonderful library , where things are handled via the
<Image>
component, which has a lot of interesting features like responsive, intrinsic etc. working out of the box.
Example:

// the settings
const imageProps = useNextSanityImage(
		configuredSanityClient,
		mySanityData.image
	);

// your responsive Image
		<Img {...imageProps} layout="responsive" sizes="(max-width: 800px) 100vw, 800px" />
	);
);
I work exclusively with this one and am super happy (the
div
-envelop helps here as well.)
Thanks for this, I was able to work around it for the moment using the div with specific css - although this library looks good and may try it.

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?