Preview pane not working for Page type in custom desk structure

15 replies
Last updated: Nov 30, 2025
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

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?