Issues with implementing custom features in Sanity v3, including accessing the sanity client within the prepare function and resolving errors with the getCliClient method...
I understand your frustration! Unfortunately, the prepare function has a significant limitation in Sanity v3: it cannot make custom GROQ queries or access the Sanity client. This is explicitly documented as a limitation of the preview system.
The prepare function only receives the selection object you define in select, and there's no second context parameter with client access. This is by design - the select syntax handles dereferencing, but only in specific, predefined ways.
Your Options
Since you need to query data (like following references in the opposite direction), here are the viable approaches:
1. Use a Custom Preview Component (Recommended)
Instead of relying on prepare, you can create a custom preview component using the Form Components API. This is a React component where you can use the useClient hook:
import {useClient} from 'sanity'
import {defineType} from 'sanity'
import React from 'react'
const CustomPreview = (props: any) => {
const client = useClient({apiVersion: '2024-01-01'})
const [parentTitle, setParentTitle] = React.useState('')
React.useEffect(() => {
client.fetch('*[references($id)][0]{title}', {id: props.id})
.then(result => setParentTitle(result?.title || ''))
.catch(() => setParentTitle(''))
}, [props.id, client])
return props.renderDefault({
...props,
title: parentTitle ? `${parentTitle} - ${props.title}` : props.title
})
}
export default defineType({
name: 'myDocument',
type: 'document',
components: {
preview: CustomPreview
},
preview: {
select: {
title: 'title',
id: '_id'
}
}
})This approach gives you full React component capabilities, including hooks like useClient, and you can use the renderDefault function to wrap the existing preview behavior while enhancing it with your custom data.
2. Use select with Direct References
If you're following a forward reference (not reverse), you can use dot notation in select to access referenced document fields:
preview: {
select: {
title: 'title',
parentTitle: 'parent.title' // Works for forward references
},
prepare({title, parentTitle}) {
return {
title: parentTitle ? `${parentTitle} - ${title}` : title
}
}
}But this only works for references defined on the document, not reverse lookups like your use case requires.
3. Denormalize Your Data
If the relationship is important for previews, consider storing the data you need directly on the document rather than requiring a reverse reference lookup. This isn't always practical, but it's the most performant option since it avoids additional queries.
Why This Changed
In v3, Sanity moved away from the parts system to a more explicit architecture. The prepare function is intentionally kept as a pure transformation function without side effects or async operations. For scenarios requiring data fetching, custom preview components with hooks are the recommended pattern - they're more powerful and align with modern React practices.
The trade-off is that custom components require more code, but they give you full control over the rendering and data fetching logic. The Form Components API provides this flexibility while maintaining type safety and integration with Studio's real-time collaboration features.
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.