Can we do asynchronous data fetching in our preview.prepare statement?
Unfortunately, no, the preview.prepare function cannot be asynchronous. This is a documented limitation in Sanity Studio – the prepare function is designed to be synchronous and cannot make custom GROQ queries or perform async operations like API calls.
However, there are several alternative approaches you can use depending on your specific use case:
Alternative 1: Custom Preview Components (Recommended)
For scenarios requiring async data fetching, you can create a custom preview component using Studio's component API. This allows you to use React hooks like useClient to fetch data asynchronously:
import {useClient} from 'sanity'
import {useEffect, useState} from 'react'
const CustomPreview = (props) => {
const client = useClient({apiVersion: '2024-01-01'})
const [asyncData, setAsyncData] = useState(null)
useEffect(() => {
// Perform your async operations here
client.fetch('*[_type == "someType"][0]')
.then(data => setAsyncData(data))
}, [props.documentId])
return props.renderDefault({
...props,
title: asyncData?.title || props.title,
subtitle: asyncData?.customField || 'Loading...'
})
}
// In your schema
export default {
name: 'myDocument',
type: 'document',
components: {
preview: CustomPreview
}
}Alternative 2: Pre-compute Values with Sanity Functions
If you're fetching data that could be computed and stored in your document, consider:
- Using Sanity Functions to listen for document changes and update computed fields
- Storing the async data as a field in your document that gets updated via a function
- Then accessing that pre-computed field in your standard
preview.prepare
This is the modern, recommended approach for automation and event handling in Sanity – Functions are serverless compute handlers that run within Sanity's infrastructure, making them ideal for reacting to content changes without needing external hosting.
Alternative 3: Use Select with References
If you're trying to access data from referenced documents, you can often use the select object with dot notation to access referenced fields synchronously:
preview: {
select: {
title: 'title',
authorName: 'author.name', // Access referenced author's name
categoryTitle: 'category.title'
},
prepare({title, authorName, categoryTitle}) {
return {
title: title,
subtitle: `By ${authorName} in ${categoryTitle}`
}
}
}Important Context from the Community
Based on discussions in the Sanity community, if you need to access the Sanity client in other schema functions (like initialValue or isUnique), you can get it from the context parameter:
initialValue: async (props, context) => {
const {getClient} = context
const client = getClient({ apiVersion: '2024-01-01'})
// Use client here
}
// For isUnique, the client is passed directly:
isUnique: async (value, {client}) => {
const slugs = await client.fetch(`*[defined(slug)].slug.current`)
return !slugs.includes(value)
}Note: The hidden property doesn't support promises, so if you need conditional visibility based on async data, you'll need to use a custom field component with conditional rendering instead.
The custom preview component approach (Alternative 1) is typically the best solution when you genuinely need async operations in previews, as it gives you full React capabilities including hooks and async/await. The trade-off is slightly more complexity, but it's the officially supported way to handle these scenarios in Studio v3.
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.