😎 Discover cool tips and tricks for customization in our next Developer Deep Dive virtual event - sign up now!

Previewing content in Sanity Studio

When editing content, it’s very useful for content creators to be able to preview how their changes will appear before publishing.

There are different approaches to previewing unpublished content depending on your use case, the technology you're building with, and how complex your content models are.

With Sanity Studio, you can accommodate previews' in several ways:

  • Generate a link in the user interface to an external preview or production environment withΒ document.productionUrlΒ in the Studio configuration
  • Build custom side-by-side previews by customizing the document view withΒ Structure Builder
  • Embed a frontend presentation tailored for previews in a document view usingΒ the Iframe Pane plugin, or a custom view component.

Previewing in your application or experience

This article mainly covers how to preview content within the studio, but is related to the topic of how to create front end previews of your content in general. To learn more about how you can use Content Lake’s Perspectives feature to selectively retrieve content as if all your drafts were published, or as if no drafts existed, please see the Presenting and Previewing Content article.

Generating a link to a production environment

You can generate a link to a front end preview environment that will appear behind the three-dotted menu above the document view.

The three-dotted menu allows you to open a generated URL in a new tab

This is done by returning a URL viaΒ document.productionUrlΒ where you configure your Studio, typically inΒ sanity.config.ts. This property accepts a callback function that is invoked with any previousΒ productionUrlΒ values as the first argument, andΒ contextΒ as it second, with useful values like the currentΒ documentΒ state,Β projectId,Β dataset,Β currentUser, a configuredΒ clientΒ for fetching referenced documents, and the Studio'sΒ schema.

You can also return a promise in case you want to fetch information from other documents or an external source to generate the URL.

// sanity.config.ts
import {defineConfig} from 'sanity'
import {deskTool} from 'sanity/desk'

export default defineConfig({
  name: 'default',
  title: 'My Cool Project',
  projectId: 'my-project-id',
  dataset: 'production',
  document: {
    // prev is the result from previous plugins and thus can be composed
    productionUrl: async (prev, context) => {
      // context includes the client and other details
      const {getClient, dataset, document} = context
      const client = getClient({apiVersion: '2023-05-31'})

      if (document._type === 'post') {
        const slug = await client.fetch(
          `*[_type == 'routeInfo' && post._ref == $postId][0].slug.current`,
          {postId: document._id}
        )

        const params = new URLSearchParams()
        params.set('preview', 'true')
        params.set('dataset', dataset)

        return `https://my-site.com/posts/${slug}?${params}`
      }

      return prev
    },
  },
})

Gotcha

Sanity Studio currently supports only one generated link. You can come into situations where plugins implement their own productionUrl setting. Depending on how you deal with the prev value, your customization might override that of a plugin.

Embedding previews in the Studio

You might have seen theΒ Preview Anything blog postΒ that features different types of previews beyond the visual representation of a website. It shows how you can preview how content appears in Search Engine Result Pages, social media, in different accessibility accommodations, and specialized renders of signage and physical publishing.

While you can use theΒ document.productionUrlΒ in the Studio configuration to generate helpful links for content creators, another approach is to add custom views to the document pane in the Studio. This is powerful, especially when you have support for real-time content previews.

You can add additional document views using theΒ Structure Builder API. While you can add views to any document node when you define document list trees, the quickest way to add views is by usingΒ the Desk toolβ€˜s defaultDocumentNode propertyΒ to return document views conditionally on a documentβ€˜s value (for example, itsΒ _type).

A documentΒ viewΒ takes any React component that receives document values for its different states in real-time (draft,Β displayed,Β historical,Β published) in props. You can use this with custom components to build previews or embed a remote preview web page in anΒ <iframe>. If you want to do the latter, we recommend checking outΒ the Iframe pane plugin available on sanity.io/exchange. Below is an example of how to implement it:

// sanity.config.ts
import {defineConfig} from 'sanity'
import {deskTool, type DefaultDocumentNodeResolver} from 'sanity/desk'
import Iframe from 'sanity-plugin-iframe-pane'
import {SanityDocument} from 'sanity'

// Customize this function to show the correct URL based on the current document
function getPreviewUrl(doc: SanityDocument) {
  return doc?.slug?.current
    ? `${window.location.host}/${doc.slug.current}`
    : window.location.host
}

const defaultDocumentNode: DefaultDocumentNodeResolver = (S, {schemaType}) => {
  // Only show preview pane on `movie` schema type documents
  switch (schemaType) {
    case `movie`:
      return S.document().views([
        S.view.form(),
        S.view
          .component(Iframe)
          .options({
            url: (doc: SanityDocument) => getPreviewUrl(doc),
          })
          .title('Preview'),
      ])
    default:
      return S.document().views([S.view.form()])
  }
}

export default defineConfig({
  // ...other config settings
  plugins: [
    deskTool({
      defaultDocumentNode,
    }),
    // ...other plugins
  ],
})

Disabling draft documents with Studio Live-edit mode

Sometimes you might not need drafts at all. Say you're using listeners to update a ranking list in an app and you just want the changes in the studio to go out in real-time on the wire as mutations as the order is being changed.

To disable drafts for a data type simply includeΒ liveEdit: trueΒ in the schema definition, for example:

export default {
  name: 'author',
  title: 'Author',
  type: 'document',
  liveEdit: true,
  …
}

Was this article helpful?