Understanding and implementing productionURL in Sanity v3 using the sanity-plugin-iframe-pane plugin.

10 replies
Last updated: Dec 19, 2022
Is there an easier way to do the productionURL? dont quite understand what i need from the guide or not.
AI Update

The productionURL you're asking about is typically used in Sanity's preview/presentation features to help the Studio know where your production website is hosted. Let me break down the easiest way to set it up.

Basic Setup

The simplest way to configure your production URL is in your sanity.config.ts when setting up the Presentation tool:

// sanity.config.ts
import {defineConfig} from 'sanity'
import {presentationTool} from 'sanity/presentation'

export default defineConfig({
  // ... other config
  plugins: [
    presentationTool({
      previewUrl: 'https://your-website.com', // Your production URL
      // Or for local development:
      // previewUrl: 'http://localhost:3000',
    }),
  ],
})

What It's For

The previewUrl (sometimes called productionURL in guides) tells Sanity Studio:

  • Where to load your website preview in the Presentation tool
  • How to construct URLs when navigating between documents
  • Where to send editors when they click "Open Preview"

Common Patterns

For different environments:

presentationTool({
  previewUrl: process.env.SANITY_STUDIO_PREVIEW_URL || 'http://localhost:3000',
})

This way you can use environment variables to switch between local and production URLs.

Don't Overthink It

If you're just getting started, simply point it to where your website runs:

  • Local development: http://localhost:3000 (or whatever port your dev server uses)
  • Production: Your actual website URL like https://mysite.com

The guides you might be reading could be showing more complex setups with document resolvers and location resolvers for advanced routing patterns, but you don't need those to get started. Just set the basic previewUrl and you should be good to go!

If you need more advanced features like automatically opening specific documents to their corresponding pages, that's where the Presentation Resolver API comes in, but start simple first.

As far as I can recall, you need to replace:
const { client, dataset, document } = context
with:

const { getClient, dataset, document } = context
Guess theres an typo in the docs then? Will try later
user E
could you help me some more? did you also mean
context
and not
content
?
Yes
user U
, apologies for the typo - fixed.
If it helps, here’s my config using Sanity v3 and the
sanity-plugin-iframe-pane
plugin.

// sanity.config.js 

import { defineConfig } from 'sanity'
import { deskTool } from 'sanity/desk'
import { schemaTypes } from './schemas'
import { structure } from './studio-config/structure'
import { defaultDocumentNode } from './studio-config/views'

export default defineConfig({
    name: 'default',
    title: <title>,
    projectId: <projectId>,
    dataset: <dataset>,
    plugins: [
        deskTool({
            structure,
            defaultDocumentNode
        }),
        visionTool()
    ],
    schema: {
        types: schemaTypes
    },
})

// ./studio-config/views

import Iframe from 'sanity-plugin-iframe-pane'

// Specify document types
const previewSchemaTypes = ['page', 'post']

export const defaultDocumentNode = (S, { schemaType }) => {
    // Add previews for specified schema types
    if (previewSchemaTypes.includes(schemaType)) {
        return S.document().views([
            S.view.form(),
            S.view
                .component(Iframe)
                .title('Preview')
                .options({
                    url: (doc) => resolveProductionUrl(doc, S.context),
                    defaultSize: 'desktop',
                    reload: {
                        button: true,
                        revision: true
                    },
                    attributes: {
                        allow: 'fullscreen' // string, optional
                    }
                })
        ])
    }
}

const resolveProductionUrl = async (doc, context) => {
    const { getClient } = context

    if (previewSchemaTypes.includes(doc._type)) {
        const client = await getClient({ apiVersion: '2022-06-07' })
        const slug = await client.fetch(`*[_id == $id][0].slug.current`, { id: doc._id })

        // Build preview url
        const url = new URL(<your_base_preview_url_goes_here>)

        // Switch for resolving doc type urls
        switch (doc._type) {
            case 'page':
                url.pathname = `/${slug}/`
                break
            case 'post':
                url.pathname = `/posts${slug}`
                break
            default:
                break
        }

        // Add preview url params
        url.searchParams.set('preview', 'true')

        return url.toString()
    }

    return doc
}
You will probably need to tweak the preview pathnames to fit your own needs here.
As per the above code snippet, here’s how I do the fetch:
const client = await getClient({ apiVersion: '2022-06-07' })
const slug = await client.fetch(`*[_id == $id][0].slug.current`, { id: doc._id })
Note I await the
getClient
before I do the fetch.
Thanks for the help
user E
! Trying to implement this later tonight
Hope all goes well
user U
- let me know how you get on 😃
Is there anything you have on each document inside deskTool to enable this preview?
And how does your imports looks like inside the
defaultDocumentNode
Currently not working, but i imagine im missing a few imports. A

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?