Using async client.fetch in custom preview component to expand nested references

2 replies
Last updated: Jan 19, 2024
Hi,is it possible to use async
client.fetch
in a custom preview component?What I need is to expand the reference A and then expand the array of references to B. And I need the data from each B document to display in the preview for the object that contains A element
AI Update

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.).

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 thread
2 replies
Answering to myself:used
client.fetch
inside
useEffect
and added the prop from prepare as a dependency to trigger an update of the preview when it's ready
You can also select the values you need from the reference. Though, if you need a lot of them it’s way less code to just create a component.

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.

Was this answer helpful?