Using async client.fetch in custom preview component to expand nested references
Yes, you can use async client.fetch in a custom preview component! However, there are important limitations with the standard preview.prepare() function that you should understand.
The Key Limitation: prepare() Cannot Be Async
The standard preview.prepare() function in your schema cannot handle async operations. This means you can't use await client.fetch() directly inside prepare(). This is a known limitation that's been discussed in the community.
Workaround: Use Custom Preview Components
The solution is to create a custom preview component instead of using the standard prepare() function. Custom preview components are React components that can handle promises and async operations, giving you full control over data fetching.
Here's how to approach your use case:
Option 1: Custom Preview Component with useClient
You can create a custom preview component that uses the useClient hook to fetch data:
import { useClient } from 'sanity'
import { useEffect, useState } from 'react'
function MyCustomPreview(props) {
const client = useClient({ apiVersion: '2023-05-24' })
const [expandedData, setExpandedData] = useState(null)
useEffect(() => {
// Fetch your expanded references
client.fetch(
`*[_id == $id][0]{
...,
"expandedA": relA->{
...,
"expandedB": relB[]->{ ... }
}
}`,
{ id: props.documentId }
).then(setExpandedData)
}, [props.documentId])
if (!expandedData) return <div>Loading...</div>
// Render your custom preview with the expanded data
return props.renderDefault({
...props,
title: expandedData.expandedA?.title,
subtitle: expandedData.expandedB?.[0]?.name
})
}Then in your schema:
{
name: 'myDocument',
type: 'document',
components: {
preview: MyCustomPreview
}
}Option 2: Select with Dereferencing
For simpler cases, you can use the select method with dereferencing using dot notation and array indices:
preview: {
select: {
title: 'title',
refATitle: 'referenceA.title', // Dereference A
refBFirst: 'referenceA.arrayOfB.0.name', // First item in B array
refBSecond: 'referenceA.arrayOfB.1.name' // Second item
},
prepare({ title, refATitle, refBFirst, refBSecond }) {
return {
title: refATitle,
subtitle: `${refBFirst}, ${refBSecond}...`
}
}
}Limitation: This only works for a fixed number of array items (you'd need arrayOfB.0, arrayOfB.1, etc.).
Recommended Approach
For your use case of expanding reference A and then expanding an array of references to B, I'd recommend the custom preview component approach. It gives you:
- Full async/await support
- Access to GROQ queries via
client.fetch() - Complete control over loading states
- Ability to handle dynamic arrays of any length
More details on custom preview components can be found in the Sanity documentation on previews.
Show original thread2 replies
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.