Presenting Images
Presenting images through the Sanity image pipeline
When you use Sanity you have a globally-distributed asset CDN at your fingertips. At any time you can request your image in a new size, with a new crop or whatever you need and the asset is created for you and automatically cached close to your users. One thing to keep in the back of your mind as you implement your front-end is that to achieve maximum performance, you should take care to reuse crops and sizes across your front-end to make sure your cached assets are re-used.
Before we go into the practical details, let's recap how images are handled in Sanity: The image
type represents an image used as a field in a document, or embedded in a block text. It may contain additional fields, like caption, crediting, etc., and may contain the specific crop and hotspot for this image. The image
references an asset
which represents the actual image data to be displayed for this image. (One asset
may actually be shared by several image
s allowing editors to reuse an asset with different crops, captions etc.)
The absolutely most basic way to present images from Sanity is to get the base url of the image from the asset. Let's say we have a document type person that has a field image, we can get the base url with this simple query:
*[_type == 'person']{ name, "imageUrl": image.asset->url }
Here we follow the reference asset
from the image and extract only the url.
{
name: "Sean Gunn",
imageUrl: "https://cdn.sanity.io/images/zp7mbokg/production/G3i4emG6B8JnTmGoN0UjgAp8-300x450.jpg"
}
Gotcha
Be aware, that requesting un-optimised images directly via the image URL, as detailed here, can lead to overages in your usage!
We can now append url-options to this base-url in order to constrain size, crop the image, blur it or perform other operations on it. Some examples:
// The base image
https://cdn.sanity.io/images/zp7mbokg/production/G3i4emG6B8JnTmGoN0UjgAp8-300x450.jpg
// Resized to have the height 200
https://cdn.sanity.io/images/zp7mbokg/production/G3i4emG6B8JnTmGoN0UjgAp8-300x450.jpg?h=200
// Extract a rectangle from the image (x, y, width, height)
https://cdn.sanity.io/images/zp7mbokg/production/G3i4emG6B8JnTmGoN0UjgAp8-300x450.jpg?rect=70,20,120,150
// Extract a rectangle from the image (x, y, width, height) and constrain height to 64
https://cdn.sanity.io/images/zp7mbokg/production/G3i4emG6B8JnTmGoN0UjgAp8-300x450.jpg?rect=70,20,120,150&h=64
// Blur the image
https://cdn.sanity.io/images/zp7mbokg/production/G3i4emG6B8JnTmGoN0UjgAp8-300x450.jpg?blur=50
Gotcha
Small images get scaled up to the width or height you specify. To avoid this use &fit=max
.
See the image url reference documentation for the full list of parameters.
Some images may have a crop and a hot-spot. This may be enabled in the schema. If there is a crop/hotspot, that means the editor has used an interface like this:
The crop describes which part of the image the editor wants to allow to be used, while the hot-spot specifies what she wants preserved when the image needs to be cropped additionally in a front-end.
An image record with a crop and hot-spot might look like this from the api:
{
_type: "image",
asset: {
_ref: "image-G3i4emG6B8JnTmGoN0UjgAp8-300x450-jpg",
_type: "reference"
},
// The crop is specified in fractions of the image dimensions
// and measured from the edge of the image. This image is cropped
// from the bottom at 44% of the image height. The other dimensions
// are left un-touched.
crop: {
bottom: 0.44,
left: 0,
right: 0,
top: 0
},
// The hot-spot position x, y is in fractions of the image dimensions.
// This hot-spot is centered at 43% of the image width from the left,
// 26% of the image height from the top. The width and height is
// in the same unit system. This hot spot is 44% of the image width wide,
// 65% of the image height tall, this rectangle is centered on the x,y
// coordinate given.
hotspot: {
height: 0.44,
width: 0.65,
x: 0.43,
y: 0.26
}
}
It is the responsibility of the front-end to respect the crop/hot-spot according to the wishes of the editor, but conveniently we provide a JavaScript library that takes care of this for you.
For JavaScript projects, we provide the @sanity/image-url
npm package that will generate image-urls for you given an image record like the one above. You may specify additional constraints like width and height and trust that the crop/hot-spot is respected where applicable.
Let's see how this package might be used with React to render images. (There is nothing React-specific about it, you can use it with any framework you desire)
You install it to your project in the usual manner:
npm install --save @sanity/image-url
Then you need to configure it with your projectId and dataset. The simplest way to do this, is to initialize it with the Sanity client you already have in your project. To do this, import it using the right path like below. In the example we have a sanityClient.js
file at the root of our project, which is assumed to return a configured client (complete with projectId
, dataset
, and apiVersion
). Read more about the client in our JavaScript client documentation.
import React from 'react'
import client from './sanityClient'
import imageUrlBuilder from '@sanity/image-url'
// Get a pre-configured url-builder from your sanity client
const builder = imageUrlBuilder(client)
// Then we like to make a simple function like this that gives the
// builder an image and returns the builder for you to specify additional
// parameters:
function urlFor(source) {
return builder.image(source)
}
Now you can use this handy builder-syntax to create your urls:
<img src={urlFor(person.image).width(200).url()} />
You can add more options like this:
<img src={urlFor(person.image).width(200).invert().flipHorizontal().url()} />
See the full list of builder options in the reference documentation.
Gotcha
To ensure that crop and hotspot settings are automatically applied, make sure to pass the entire image field (image record) to the image builder.
You can still append additional values from the asset, like metadata.lqip
for generating blurHash URLs or alt text from the asset itself. However, it's important not to modify or remove any existing field values if you want the editor’s adjustments to be reflected.
While the image-builder library handles the heavy lifting by generating the optimized image URL from the image record (with the asset reference intact), you can still extract useful details from your image in your query. Just be sure to leave the asset
reference, hotspot
, crop
, and other related fields unchanged.
// in your query
image {
..., // this will ensure you keep the existing data
...asset-> {
altText,
caption,
...metadata {
lqip, // the lqip can be used for blurHashUrl or other low-quality placeholders
...dimensions {
width,
height
}
}
}
}
To support downloading an image with a specific filename in a front end, you need to append the dl=
query parameter to the URL, e.g. https://<some-image-url>&dl=
. By default this will use the filename the image had when it was uploaded, if storeOriginalFilename
was not disabled.
You can also specify a filename by including it in the URL like so https://<some-image-url>&dl=<filename-of-your-choice>
. The asset id will be used as the filename if there is no original filename or a filename was not provided in the URL.