How to resolve references in resolveProductionUrl for preview iframe?

11 replies
Last updated: Jan 28, 2026
Hi!
I'm setting up a preview iframe pane, and creating a
resolveProductionUrl
function, like in many of the guides suggests. I have done it before, and it works great. Usually.
However, now I want to construct the URL from a referenced field. I have an
article
schema type, that has a reference to a
category
type. The URL is basically
${article.category.slug.current}/${article.slug.current}
. However, the doc passed by the
S.view.options({ url: resolveProductionUrl })
function (where S is the structure builder) doesn't resolve references (so
article.category
is just a regular ref object.
How can I resolve that doc?
AI Update

The document passed to resolveProductionUrl doesn't include resolved references by default - you're absolutely right. You need to fetch the full document data yourself using Sanity's client within the resolveProductionUrl function.

The key is making resolveProductionUrl an async function and using a GROQ query with the reference operator (->) to dereference the category:

import { client } from './sanityClient' // Your configured client

export const resolveProductionUrl = async (doc) => {
  if (doc._type !== 'article') return undefined
  
  // Fetch the document with the resolved category reference
  const article = await client.fetch(
    `*[_id == $id][0]{
      "categorySlug": category->slug.current,
      "articleSlug": slug.current
    }`,
    { id: doc._id }
  )
  
  if (!article?.categorySlug || !article?.articleSlug) return undefined
  
  const baseUrl = 'https://your-site.com' // or process.env.NEXT_PUBLIC_SITE_URL
  return `${baseUrl}/${article.categorySlug}/${article.articleSlug}`
}

The category-> syntax in the GROQ query tells Sanity to resolve the reference and fetch the actual category document. Then category->slug.current gets the slug from that resolved document.

A few things to note:

  1. Make it async: The function needs to be async since you're fetching data
  2. Use the document's _id: The doc parameter has the _id, which is stable and works even for drafts
  3. Handle missing data: Always check that both slugs exist before constructing the URL
  4. Consider drafts: If you want preview URLs for draft documents, you might need to query *[_id in [$id, "drafts." + $id]] to handle both published and draft versions

If you're using the sanity-plugin-iframe-pane, the same approach works - just pass your async resolveProductionUrl function to the plugin's url option.

Show original thread
11 replies

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?