sanity
sanity copied to clipboard
Make it possible to get width and height from imageUrlBuilder
Is your feature request related to a problem? Please describe. When for example lazy loading and/or animating images on a website, you often need to specify the width and height of the image.
This is currently hard/impossible to do when using the imageUrlBuilder from @sanity/image-url because you don't know what actual dimensions the image will have when it comes back after resizing and cropping.
For example in this case, I would only know the width, and not the resulting height:
<img
key={project.image.asset._ref}
src={imageUrlBuilder(config).image(project.image)
.width(400)
.format('jpg')
.auto('format')
.quality(80)
.url()}
/>
You can get the original width and height from the asset ref, but that doesn't really help because the image may have been cropped, and being resized it won't be correct anyways.
Describe the solution you'd like
I'd like a an alternative to .url() named .props() which returned an object with src, height, and width, where height and width was calculated the exact same way as the Sanity image API does, meaning the height and width would match the exact height and width of the image returned from the url. Returning an object like that means you could both access the height and width as you want, but also that you could easily spread it directly onto an img tag:
<img
key={project.image.asset._ref}
{...imageUrlBuilder(config).image(project.image)
.width(400)
.format('jpg')
.auto('format')
.quality(80)
.props()}
/>
Describe alternatives you've considered Calculating the resulting height and width manually would be both hard and error-prone. Don't even know how I'd begin to do that with all the hotspot, crop, and size parameters... 😕
This is an interesting suggestion. We'll look into it when we iterate on the image-url builder!
function croppedDimensions(height, width, crop, croppedWidth){
if(crop){
const y = crop.top + crop.bottom
const x = crop.left + crop.right
height = (1-y) * height
width = Math.round((1-x) * width)
}
height = croppedWidth ? Math.round(height * croppedWidth / width) : Math.round(height)
return {height, width: croppedWidth ?? width}
}
You can use this function to get the height and width. Where "crop" is the crop object stored in a sanity image. Rest of the values are in pixels.
I compute the post-crop dimensions in advance using the original image dimensions, the desired width and height settings, and the crop values supplied via Sanity using the code below in gatsby-plugin-sanity-image:
const cropWidth =
dimensions.width -
asset.crop.left * dimensions.width -
asset.crop.right * dimensions.width
const cropHeight =
dimensions.height -
asset.crop.top * dimensions.height -
asset.crop.bottom * dimensions.height
cropRatio = cropWidth / cropHeight
if (cropRatio > origRatio) {
maxHeight = cropHeight
} else {
maxWidth = cropWidth
}
(source)
Note that this isn't going to take into account your fit mode (though gatsby-plugin-sanity-image does that as well) or account for a manually applied rect param (the image-url library converts a crop into rect params, so you'd be overriding it, though you can handle the logic yourself to stack them).
Any updates on this?
In case anyone is using this with Next.JS, I found a library that does this automatically for you. https://github.com/bundlesandbatches/next-sanity-image
Bump!
Plus one for that.
I thought we would have a way of getting all image information like we can do via GROQ but we can't. :/
It would be nice to have a method like: image.info() or image.meta() and get everything we get when we do:
"image": image.asset ->
+1 here as well, or at least a well explained "proper" way to do this... it's best practice now to have w/h on images and when it's just an asset reference we are stuck making another API call just for this information
@pm0u For what it is worth, if you're using CSS to size your images, the width and height properties only need to convey the aspect ratio to the browser. Unless you're cropping the image, using the original width and height supplied via the image _id or _ref would work for this purpose.
@coreyward Being able to crop the images, with selected focus points and such, is part of the "point" with sanity images though.
It should be a proper and stable way to get the original height, width, and aspect ratio, as well as cropped height, width, and aspect ratio, without hacks.
@coreyward I looked into how next-sanity-image was accomplishing this and they use those dimensions, I am building a similar helper to work with picture elements. I am planning to use those for now, although I have to say I'm never excited about using a filename as a source of truth as there can be lots of things that I could see breaking this functionality. Perhaps those concerns are moot if the naming conventions of sanity are static, but I wouldn't necessarily expect that to be guaranteed. If it's already there in the filename, it seems like it should be easy enough to expose these values in a future proof way.
@pm0u The Sanity docs do say that this ID format is stable and consistent, and in fact use this information in the Studio and elsewhere similarly.
@Svish Agreed! It would make my work on gatsby-plugin-sanity-image much easier as well!
@coreyward thanks for pointing that out, I finally found that mentioned here: https://www.sanity.io/docs/image-urls which I hadn't read before. Wish it was a bit more prominent but good to know!
HI, I know this is a late answer but you can actually get the image dimensions pretty easily nowadays:
import { getImageDimensions } from "@sanity/asset-utils";
const { width, height } = getImageDimensions(image);
// in Next 14.1 with the Image component
return (
<Image
className="w-full rounded-lg"
src={urlFor(image).auto("format").fit("max").url()}
alt={image.alt || "No alt-tag provided"}
width={900}
height={900 * (height / width)}
sizes="
(max-width: 768px) 95vw,
(max-width: 1200px) 60vw,
40vw"
/>
);
Also see this article on The best Next.js & Sanity <Image/> Component.
Hope this helps someone!
@nikolailehbrink That returns the dimensions of the uploaded image, not the output image (post-crop, post-transformations).
If anybody wants to determine the output dimensions deterministically, take a look at sanity-image. It does not use this library to build URLs, but instead builds the URL from your parameters using its own approach in a way that allows for predicting reliably the output dimensions across all cases. You can use it as either a React component or as a function that returns the dimensions and a full URL to the image, or as a function that returns an entire src-set for you.
Hi @coreyward! Thanks for the correction. Tried it out and know now what the actual issue here is about!