Question about desk structure and refreshing when new documents are created.

4 replies
Last updated: Aug 24, 2023
Hey all! I've got a question about desk structure. I'm trying to create a list of documents without the top-level "add new document" and "menu" items (working around i18n not working for
documentID
for one-off content types). I've got it working querying for the content type I want and building, but it doesn't refresh when a new document that matches that criteria is created. Here's a snippet of the code:
const microcopy = await context
    .getClient({ apiVersion: '2023-08-24' })
    .fetch('*[_type == "microcopy"]');
Later, in the desk builder:

S.list().items(microcopy.map((item) =>
      S.documentListItem().id(item._id).schemaType(type))
AI Update

The issue you're running into is a common gotcha with Structure Builder: when you fetch documents at configuration time using context.getClient().fetch(), that query runs once when the structure is built, not dynamically as content changes. The structure configuration is essentially static once it's created, so new documents won't appear until you refresh the entire Studio.

The solution is to use S.documentTypeList() or S.documentList() instead of manually fetching and mapping documents. These methods create dynamic lists that automatically refresh when documents are created, updated, or deleted.

Here's how to refactor your code:

S.list()
  .title('Microcopy')
  .items([
    S.listItem()
      .title('All Microcopy')
      .child(
        S.documentList()
          .title('Microcopy')
          .filter('_type == "microcopy"')
          .menuItems([]) // Removes the "Create new" menu item
      )
  ])

Or even simpler, if you just want a filtered list without the top-level menu:

S.documentList()
  .title('Microcopy')
  .filter('_type == "microcopy"')
  .menuItems([]) // This removes the top-level menu items
  .canHandleIntent(() => false) // Optional: prevents this list from handling create intents

The key differences:

  • S.documentList() creates a live-updating list that queries on-demand, not at build time
  • .menuItems([]) removes the "Create new" and menu buttons you mentioned wanting to hide
  • The list will automatically refresh when documents matching your filter are created or modified

If you need more complex filtering or grouping, you can still use GROQ filters with S.documentList() - they'll be evaluated dynamically. For example:

S.documentList()
  .filter('_type == "microcopy" && defined(key)')
  .params({}) // Add params here if using variables in your filter

The Structure Builder guide has more examples of creating filtered and grouped lists that update dynamically, including how to organize documents by date, status, or other properties.

Sounds like you will need to add some sort of listener for events when new documents are added. Perhaps take a look at the useDocumentStore hook?
Returns an observable that can be used to listen for changes and events to documents in the current project.
Thanks, unfortunately because this is my desk structure it doesn't appear to be a React component I can use hooks with?
The context has documentStore, but I can't use the
useMemoObservable
to get the value of the observable
Ok! I got it working! Your tip on
documentStore
is what sealed it. Turns out there's a sample of this working inside a studio test
https://github.com/sanity-io/sanity/blob/c5bfa12f8f5e8caffe717287b81cc5d0a43e231e/dev/test-studio/structure/resolveStructure.ts#L430C10-L450

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?