Preview pane not working for Page type in custom desk structure

15 replies
Last updated: Feb 28, 2023
I need some help with getting my preview pane working for certain document types. Currently, my preview for my Global Settings type is working, but I can not get the the Page type to display a preview pane. Here is a paired down version of my deskstructure.js:
import LoeSettingsPreview from "./preview-components/LoeSettingsPreview";
import PagePreview from "./preview-components/PagePreview";

export const loeStructure = (S, context) =>
  S.list()
  .title('LOE Content')
  .items([
      S.listItem()
        .title("Global Settings")
        .schemaType("loeSetting")
        .icon(MdSettings)
        .child(
          S.editor()
            .schemaType("loeSetting")
            .documentId("loeSetting")
            .views([
              S.view.form(),
              S.view.component(LoeSettingsPreview).title("Preview"),
            ])
        ),
      S.listItem()
        .title("Pages")
        .icon(SiLibreoffice)
        .child(
          S.list()
            .title("Filters")
            .items([
              S.listItem()
                .title("Pages By Microsite")
                .icon(SiMoleculer)
                .child(
                  S.documentTypeList("microsite")
                    .title("Pages By Microsite")
                    .child((micrositeId) =>
                      S.documentList()
                        .title("Pages")
                        .filter(
                          '_type == "page" && _id in *[_id == $micrositeId ][0].content[].docId._ref'
                        )
                        .params({ micrositeId }),
                    )
                ),
              S.listItem()
                .title("Approved Pages")
                .icon(MdThumbUpAlt)
                .child(
                  S.documentList()
                    .title("Approved Pages")
                    .filter('_type == "page" && status == "approved"')
                ),
              S.listItem()
                .title("Draft Pages")
                .icon(MdEditNote)
                .child(
                  S.documentList()
                    .title("Draft Pages")
                    .filter(
                      '_type == "page" && (status == "draft" || !defined(status)) '
                    )
                ),
              S.listItem()
                .title("Expired Pages")
                .icon(MdDelete)
                .child(
                  S.documentList()
                    .title("Expired Pages")
                    .filter('_type == "page" && status == "expired"')
                ),
              S.listItem()
                .title("All Pages")
                .child(
                  S.documentTypeList("page")
                  .title("Pages")
                ),
          ]),
        ),
As this stands, there is no Editor/Preview toggle for Pages like there is for my Global Settings. I am expecting something like the attached image. This is working in our v2 but I am currently trying to migrate to v3. Thanks!
AI Update

I can see the issue! In your desk structure, you're setting up custom views for the Global Settings document (which works), but for Pages, you're only defining list items and filtered document lists without specifying the views for individual page documents.

The key missing piece is a defaultDocumentNode resolver that tells Sanity Studio what views to show when opening a Page document. Here's how to fix it:

import LoeSettingsPreview from "./preview-components/LoeSettingsPreview";
import PagePreview from "./preview-components/PagePreview";

export const loeStructure = (S, context) =>
  S.list()
    .title('LOE Content')
    .items([
      S.listItem()
        .title("Global Settings")
        .schemaType("loeSetting")
        .icon(MdSettings)
        .child(
          S.editor()
            .schemaType("loeSetting")
            .documentId("loeSetting")
            .views([
              S.view.form(),
              S.view.component(LoeSettingsPreview).title("Preview"),
            ])
        ),
      S.listItem()
        .title("Pages")
        .icon(SiLibreoffice)
        .child(
          S.list()
            .title("Filters")
            .items([
              // ... your existing list items
            ])
        ),
    ]);

// Add this defaultDocumentNode resolver
export const loeDefaultDocumentNode = (S, { schemaType }) => {
  if (schemaType === 'page') {
    return S.document().views([
      S.view.form(),
      S.view.component(PagePreview).title("Preview"),
    ]);
  }
  
  return S.document();
};

Then in your sanity.config.js (or .ts), you need to pass both the structure AND the defaultDocumentNode to your desk configuration:

import { loeStructure, loeDefaultDocumentNode } from './deskStructure';

export default defineConfig({
  // ... other config
  plugins: [
    structureTool({
      structure: loeStructure,
      defaultDocumentNode: loeDefaultDocumentNode,
    }),
  ],
});

The defaultDocumentNode resolver is what controls the views for documents when they're opened from lists. Your Global Settings works because you explicitly defined the views in the .child() call with S.editor().views(), but for Page documents that come from document lists (like "All Pages", "Draft Pages", etc.), you need the defaultDocumentNode to specify those views.

This is a common migration gotcha from v2 to v3 - the structure API is similar but the configuration pattern in the config file changed slightly. The key difference is that individual document views for list items need to be handled by defaultDocumentNode rather than being inherited automatically.

Show original thread
15 replies
It looks like you’re currently only adding the views to your global settings doc in that structure. Can you try adding a default document node function?
Yes, I did it that way at one point and am now doing it this way 🤷‍♀️ I am getting the Edit and Preview options now, but it seems that there is an error in how that translates over to my 11ty code, so now I think my issue isn't sanity-related (or so I think HA!)

S.listItem()
        .title("Pages")
        .icon(SiLibreoffice)
        .child(
          S.list()
            .title("Filters")
            .items([
              S.listItem()
                .title("Pages By Microsite")
                .icon(SiMoleculer)
                .child(
                  S.documentTypeList("microsite").title("Pages By Microsite")
                    .child((micrositeId) =>
                      S.documentList()
                        .title("Pages")
                        .filter(
                          '_type == "page" && _id in *[_id == $micrositeId ][0].content[].docId._ref'
                        )
                        .child(
                          S.editor()
                            .schemaType("page")
                            .documentId("page")
                            .views([
                              S.view.form(),
                              S.view.component(PagePreview).title("Preview"),
                            ])
                        )
                        .params({ micrositeId })
                    )
                ),
              S.listItem()
                .title("Approved Pages")
                .icon(MdThumbUpAlt)
                .child(
                  S.documentList()
                    .title("Approved Pages")
                    .filter('_type == "page" && status == "approved"')
                    .child(
                      S.editor()
                        .schemaType("page")
                        .documentId("page")
                        .views([
                          S.view.form(),
                          S.view.component(PagePreview).title("Preview"),
                        ])
                    )
                ),
              S.listItem()
                .title("Draft Pages")
                .icon(MdEditNote)
                .child(
                  S.documentList()
                    .title("Draft Pages")
                    .filter(
                      '_type == "page" && (status == "draft" || !defined(status)) '
                    )
                    .child(
                      S.editor()
                        .schemaType("page")
                        .documentId("page")
                        .views([
                          S.view.form(),
                          S.view.component(PagePreview).title("Preview"),
                        ])
                    )
                ),
              S.listItem()
                .title("Expired Pages")
                .icon(MdDelete)
                .child(
                  S.documentList()
                    .title("Expired Pages")
                    .filter('_type == "page" && status == "expired"')
                    .child(
                      S.editor()
                        .schemaType("page")
                        .documentId("page")
                        .views([
                          S.view.form(),
                          S.view.component(PagePreview).title("Preview"),
                        ])
                    )
                ),
              S.listItem()
                .title("All Pages")
                .child(
                  S.documentTypeList("page") .title("Pages")
                  .child(
                    S.editor()
                      .schemaType("page")
                      .documentId("page")
                      .views([
                        S.view.form(),
                        S.view.component(PagePreview).title("Preview"),
                      ])
                  )
                ),
          ]),
It almost seems like no data is getting sent to the preview. I noticed this when I did the getDefaultDocumentNode option, too. The preview works great in v2, but now that I am trying to migrate to v3, my page-preview is unhappy. I am wondering if the way these previews are set up if it's not actually telling it what to preview?
async function getBody(pageJson) {
  let body = `<html><head><title>Not a Page</title></head><body>document ${pageJson._id} is of type ${pageJson._type}. Type "page" is required.</body></html>`;
  if (pageJson?._type != 'page') {
    return body;
  }

  //second argument is telling 11ty not to create or pull values from cache

  pageJson.body = await FullPageJson(pageJson.body, false);

  body = `<html DOCTYPE!>
    <head>
    <title>${pageJson.title}</title>
    <meta charset="UTF-8" />
    ${getCSS(pageJson)}   
    </head>
    <body>
    ${pageJsonScriptTag(pageJson)} 
   
    ${await PageBody(pageJson, false)}
    
    ${socketScriptText}
    </body>`;
  return body;
}
So, I don’t know much about 11ty 😅
What does your
productionUrl
function look like? The issue may be there.
Good question.... I don't have a productionUrl anywhere... 😬
Ok, so for some more context... I have a custom action for pages that allows our editors to open the preview in a new browser tab, and that is working just fine and goes to a url like this: https://loe-sanity-preview.netlify.app/.netlify/functions/page-preview?slug=740fd4c8-1d18-48c8-b95d-2269b03d0c9a
And the preview pane should just be an iframe with this exact url
But, instead, it is like the information isn't going through or something is getting lost in translation
It sounds like something’s going wrong in the document action, then. We do have a built in API for previews that may be more reliable. But if you don’t want to migrate to using that, can you share the document action?
This is my PreviewLink document action

import { BsBoxArrowUpRight } from "react-icons/bs";

export function PreviewLink(props) {
    let previewStart = "<https://loe-sanity-preview.netlify.app/.netlify/functions/page-preview?slug=>"
    let pageId = props.id
    let previewLink = previewStart + pageId

    function openLink() {
        window.open(previewLink, '_blank')
    }

    return {
        label: "External Preview",
        icon: BsBoxArrowUpRight,
        onHandle: () => openLink()
    }
  }
Got my page preview working
What turned out to be the issue?
It was related to a URL issue but we have our preview being built a slightly different way, so I just had to give that a little edit magic. Then another document type wasn't previewing either, but it turned out that the page id wasn't getting passed into the url - found that the documentid() was being called twice for that document type in the deskstructure
Awesome! Thanks for sharing and glad you got it sorted out!

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?