Setting the file. One moment.
Skill 05 · Sanity Best Practices
Subchapter 5.9
references/image.mdMarkdown3 KBView on GitHub
Always enable hotspot: true. This allows editors to control cropping and the focal point.
defineField({
name: 'mainImage',
title: 'Main Image',
type: 'image',
options: {
hotspot: true // CRITICAL
},
fields: [
defineField({
name: 'alt',
type: 'string',
title: 'Alternative Text',
validation: rule => rule.required().warning('Alt text is important for SEO')
})
]
})Use the Sanity Image URL Builder to generate optimized URLs (resize, crop, format).
Setup (sanity/lib/image.ts):
import createImageUrlBuilder from '@sanity/image-url'
import { dataset, projectId } from '../env'
const builder = createImageUrlBuilder({ projectId, dataset })
export const urlFor = (source: any) => {
return builder.image(source)
}Usage: The URL builder automatically uses hotspot/crop data when available:
const imageUrl = urlFor(mainImage)
.width(800)
.height(600)
.fit('crop') // Respects hotspot when cropping
.url()Create a reusable SanityImage component that handles the urlFor logic and next/image props.
import Image from 'next/image'
import { urlFor } from '@/sanity/lib/image'
interface SanityImageProps {
value: any // SanityImageSource
width?: number
height?: number
className?: string
priority?: boolean
}
export function SanityImage({ value, width = 800, height, className, priority }: SanityImageProps) {
if (!value?.asset) return null
return (
<Image
className={className}
src={urlFor(value)
.width(width)
.height(height || Math.round(width / 1.5)) // Default aspect ratio if no height
.url()}
alt={value.alt || ''}
width={width}
height={height || Math.round(width / 1.5)}
priority={priority}
// Optional: Use LQIP (Low Quality Image Placeholder)
placeholder={value.asset.metadata?.lqip ? 'blur' : 'empty'}
blurDataURL={value.asset.metadata?.lqip}
/>
)
}Critical: LQIP (Low Quality Image Placeholder) is not automatic. You must explicitly query it via asset->{ metadata { lqip } }.
mainImage {
asset->{ _id, url },
alt
}mainImage {
asset->{
_id,
url,
metadata {
lqip, // Base64 blur placeholder
dimensions { width, height } // For aspect ratio
}
},
alt,
hotspot, // Include if using hotspot cropping
crop // Include if using cropping
}Why this matters: Without querying metadata.lqip, the blurDataURL in your component will be undefined and the blur effect won’t work.
.format('webp') manually in most cases, but next/image handles this too)..width() and .height() in urlFor. Don’t download a 4000px image for a thumbnail.