next-sanity
next-sanity copied to clipboard
Supply Image component using next/image
It would be great to have an Image component built-in the library, using next/image
behind the scenes. It should be possible to use Sanity with it as since [email protected]
, there is a new loader
prop (see next/image
documentation).
At least, if not supplying a component leveraging next/image
, it would be great to have a code example on how to implement one in the docs or in an example project.
I started developing one myself as a small POC (supports both crop and hotspot features if layout !== 'fill'
):
import React, { useCallback } from 'react'
import NextImage, { ImageProps as NextImageProps, ImageLoader } from 'next/image'
import { urlFor } from '../../../clients/sanity'
import { SanityAsset } from '../../../utils/sanity'
// Needed to mostly copy ImageProps from next/image as otherwise Typescript types were all messy
export type ImageProps = Omit<
JSX.IntrinsicElements['img'],
'src' | 'srcSet' | 'ref' | 'width' | 'height' | 'loading' | 'style'
> & {
src: {
alt?: string
asset: SanityAsset & { url?: string }
}
quality?: number | string
priority?: boolean
loading?: NextImageProps['loading']
unoptimized?: boolean
objectFit?: NextImageProps['objectFit']
objectPosition?: NextImageProps['objectPosition']
} & (
| {
width?: never
height?: never
/** @deprecated Use `layout="fill"` instead */
unsized: true
}
| {
width?: never
height?: never
layout: 'fill'
}
| {
width: number | string
height: number | string
layout?: 'fixed' | 'intrinsic' | 'responsive'
}
)
/**
* Basic component displaying an Image coming from Sanity using next/image.
*
* Supports Crop/Hotspot if height and width are specified (if using a `layout` prop different of "fill")
*
* @see https://nextjs.org/docs/api-reference/next/image
*/
export const Image: React.FC<ImageProps> = ({ src, ...props }) => {
if (!src?.asset?._ref && !src?.asset?.url) {
console.warn('No Reference passed to image. Make sure the image is correctly set.')
return null
}
// Loader for Sanity. Unfortunately, as we need crop/hotspot properties here, along with the height provided,
// We need to have this function defined in the component.
const sanityLoader = useCallback<ImageLoader>(
({ width, quality = 75 }) => {
const renderWidthInt = _getInt(props.width)
const renderHeightInt = _getInt(props.height)
const imageRatio =
renderWidthInt && renderHeightInt ? renderWidthInt / renderHeightInt : undefined
let urlBuilder = urlFor(src)
.auto('format') // Load webp if supported by browser
.fit('max') // Don't scale up images of lower resolutions
.width(width)
.quality(quality)
if (renderHeightInt && imageRatio) {
urlBuilder = urlBuilder.height(width / imageRatio)
}
return urlBuilder.url() || ''
},
[src, props.width, props.height],
)
return (
<NextImage
{...props}
alt={src.alt || ''}
src={src.asset._ref || src.asset.url || ''}
loader={sanityLoader}
/>
)
}
const _getInt = (x: string | number | undefined): number | undefined => {
if (typeof x === 'number') {
return x
}
if (typeof x === 'string') {
return parseInt(x, 10)
}
return undefined
}
Having this this SanityImage
included in this toolkit could be really usefull! Or at least expose a next/image
loader to build the image path given the image constraint.
If this is something wanted I could start a PR to implement it.
next-sanity-image provides support for Sanity images with Next. It could be used as a basis for implementing the component here.
In the meantime you can use that library directly if you're looking for support via next/image.
Hey, like @gpoole mentions there's the excellent next-sanity-image
package by @lorenzodejong that provides this functionality :D In fact, we use it on www.sanity.io 🎉
@armandabric I'm sure they'll give you a warm welcome if you have ideas for further improvements 😌✨
@stipsan pretty awesome to hear you guys are using it on Sanity.io!
I've actually released version 6.0.0 recently which improves compatibility with the most recent versions of @sanity/client
and next
. This should in turn also improve compatibility with next-sanity
.
Let me know if you run into anything with the setup, i'd be happy to help you out and/or provide further improvements on the library.
Thanks @lorenzodejong, we'll be sure to let you know 😌
And it goes for you as well. If you want help to get in our automated tooling. Like, if you want to use our semantic-release
so that you don't have to make manual GitHub release notes and CHANGELOG.md
updates anymore. Or @sanity/pkg-utils
so you don't have to deal with rollup.config.ts
files or how to setup pkg.exports
we'd be happy to send you a PR 😄
@stipsan that would actually be great! The most recent releases were almost always due to a major dependency update, either on the @sanity/client
side or the next
side. I was already thinking about setting up a release pipeline, however it might be nice if we can align it further with Sanity's semantic release setup.
Would be happy to help! Our automation setup is pretty sweet, removes a whole lot of churn and it's fun to see robots do all the heavy lifting :D