Preview subtitle with async referenced document titles in Sanity

2 replies
Last updated: Feb 1, 2021
I’ve been fighting for a few hours with a preview, and I figured I’d check in here. I am creating a grid container that has a content array that will accept references to tools, or to cards. I would like for the preview to show the title, and have a subtitle that shows a few of the referenced tools or cards.
I have created a function that returns the correct values, but it doesn’t actually work in the preview because the return is an object. It’s also really gross, and I think that there has to be a better way to do this, and I am hopeful that someone can help me to find it.

Here’s the preview:

preview: {
    select: {
        title: 'title',
        content: 'content'
    },
    prepare({ title, content }) {
        const subs = previewReference(content)
        console.log('subs: ', subs)

        return {
            title: `${title}`,
            subtitle: subs
        }
    }
}
And here’s the function:

export const previewReference = async(content) => {
    const query = `*[_type == "tool"]{ _id, title }`
    return await client.fetch(query)
    .then(doc => {
        const ids = []
        doc.map(id => ids.push(id))
        return ids
    })
    .then(ids => {
        const filteredArray = []
        content.filter((value, i) => {
            const helper = value.tool._ref

            ids.filter(id => {
                const eyeDee = id._id
                if (eyeDee === helper) {
                    console.log("Got one! ", id.title)
                    filteredArray.push(id.title)
                }
            })
        });

        console.log('filteredArray: ', filteredArray)
        return filteredArray
    })
}
AI Update

I can see the issue you're running into! The prepare function in Sanity's preview configuration cannot be async and cannot make queries. The function needs to return synchronously, which is why your async function returning a Promise object isn't working.

The good news is that Sanity has a built-in solution for this exact scenario. You can use dot notation in the select object to access fields from referenced documents directly, without needing to make separate queries.

Here's how to fix your preview:

preview: {
  select: {
    title: 'title',
    // Access the title from the first 3 tools/cards in your content array
    tool0: 'content.0.tool.title',
    tool1: 'content.1.tool.title', 
    tool2: 'content.2.tool.title',
    card0: 'content.0.card.title',
    card1: 'content.1.card.title',
    card2: 'content.2.card.title'
  },
  prepare({ title, tool0, tool1, tool2, card0, card1, card2 }) {
    // Filter out undefined values and combine
    const items = [tool0, tool1, tool2, card0, card1, card2].filter(Boolean)
    
    return {
      title: title,
      subtitle: items.length > 0 ? items.join(', ') : 'No items yet'
    }
  }
}

This approach:

  • Uses array indices (content.0, content.1, etc.) to access specific array items
  • Uses dot notation to reach into the referenced documents and grab their titles
  • Works synchronously, so the preview updates immediately
  • No client queries needed!

Important limitation: You need to specify the exact number of items you want to preview upfront (I used 3 in the example). The select object can't dynamically loop through all array items, so you'll need to decide how many references you want to show in the subtitle.

If you have a mixed array of tools and cards, you might need to adjust the select paths based on your schema structure. For example, if your content array has objects with either a tool or card reference field, the above should work. If the structure is different, adjust the paths accordingly.

This is the standard Sanity way to handle reference previews - much cleaner than trying to fetch data in the prepare function!

Show original thread
2 replies
As I had suspected, I was seriously overthinking this. My main man
user A
saved the day on this one, and really helped me to find the solution. It was much simpler than I thought. Thanks a bunch for that, Man!
It’s in the docs, and I wasn’t seeing things clearly.
https://www.sanity.io/docs/previews-list-views
Solution is:

preview: {
    select: {
        title: 'title',
        content0: 'content.0.tool.title',
        content1: 'content.1.tool.title',
        content2: 'content.2.tool.title',
        content3: 'content.3.tool.title',
    },
    prepare({ title, ...content }) {
        const subs = Object.values(content).filter(Boolean);

        return {
            title: `${title}`,
            subtitle: subs.join(', '),
        }
    }
}
Thanks again, Geoff! I really do appreciate it!
It was my pleasure, KJ. I’m glad you got it working and enjoyed our discussion. Truly, though, that was all you. 😄

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?