Delegate schema preview to field's preview in Sanity

5 replies
Last updated: Aug 27, 2021
Hi all. Is there a way to "delegate" a schema's preview to one of its field? For instance, if a
Song
document were to have a field
artist
of document type
Artist
, how can I write a preview for
Song
such that it uses the preview of
Artist
to preview its
artist
field without copying over the code?
AI Update

Great question! Unfortunately, there isn't a built-in way to directly "delegate" or reuse the preview configuration from a referenced document type in Sanity. Each schema type needs to define its own preview logic.

However, you have a few practical approaches to solve this without duplicating code:

1. Dereference fields in select (simplest approach)

You can access fields from referenced documents using dot notation in your select object. This is covered in the preview configuration docs:

preview: {
  select: {
    title: 'title',
    artistName: 'artist.name',
    artistImage: 'artist.image'
  },
  prepare({ title, artistName, artistImage }) {
    return {
      title: title,
      subtitle: `by ${artistName}`,
      media: artistImage
    }
  }
}

This lets you pull specific fields from the Artist document, but you're manually choosing which fields to display rather than reusing the entire preview logic.

2. Extract shared preview logic into a helper function

If you want to reuse the same preview logic across multiple schema types, create a shared function:

// previewHelpers.js
export const artistPreviewConfig = {
  select: {
    name: 'name',
    image: 'image'
  },
  prepare({ name, image }) {
    return {
      title: name,
      media: image
    }
  }
}

// artist.js
export default {
  name: 'artist',
  type: 'document',
  preview: artistPreviewConfig
}

// song.js - reuse for the artist field
export default {
  name: 'song',
  type: 'document',
  preview: {
    select: {
      title: 'title',
      artistName: 'artist.name',
      artistImage: 'artist.image'
    },
    prepare({ title, artistName, artistImage }) {
      return {
        title: title,
        subtitle: artistName,
        media: artistImage
      }
    }
  }
}

3. Custom preview component (most flexible)

For more complex scenarios, you can create a custom preview component using React. This gives you full control over rendering, though it's more work:

const CustomPreview = (props) => {
  return props.renderDefault({
    ...props,
    title: props.schemaType?.title,
    // custom logic here
  })
}

export default {
  name: 'song',
  components: {
    preview: CustomPreview
  }
}

The reality

As discussed in this community thread, there's no way to truly "delegate" to another type's preview and automatically inherit its media/icon logic. You'll need to implement the logic yourself, either by dereferencing specific fields or creating shared helper functions.

The dereferencing approach (option 1) is usually the most practical - it's simple, doesn't require extra abstractions, and works well for most use cases where you just want to show some info about the referenced document.

Show original thread
5 replies
Hey User! Here’s a guide for configuring your preview to show fields from referenced documents .
Thanks
user M
! I had read through this already, but I don't want to show fields from referenced documents, I want to delgate the presentation that's defined in the referenced document. Whichever manner the referenced document's presentation is defined (with or without
media
, for instance), I want to have that same presentation without copying the code for the referenced document's presentation over and manually nesting the paths. Does this make sense?
I’m afraid I don’t understand, actually! Are you trying to have the referenced document define the preview on the document doing referencing, so that they effectively have the same preview?
That's it. So with type
A
having a field
b
referencing
B
, I would like an instance of
A
to preview just as if I was previewing its field
b
. But do it in such a way that the preview definition of
A
knows treats the logic for the preview of
B
as a black box.
Without the latter constraint I could just duplicate the code, and prefix all select paths with
b.
, but this create a rather tricky situation for code maintenance as my intention is now non-trivially duplicated.
Now that I say it out loud, I suppose I could define the preview logic as a reusable function:


const previewB = (prefix = "") => ({
  select: {
    title: `${prefix}someTitleField`,
    /* ... */
  },
  /* ... prepare ... */
})
And then evaluate this without arguments for
B
and with prefix
b.
for
A
.
Ah, got it! Thanks for bearing with me while I tried to understand! The one thing I think you're going to struggle with here is that the
preview
function cannot handle async and you would need that to get the details of the referenced document.

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?