Hide "Create new" button in filtered Sanity desk structure list

9 replies
Last updated: Jan 28, 2026
Hey all, I have a desk structure where documents are filtered and grouped by date into three distinct lists: upcoming, past and unfiltered.
Is there a way to get rid of the
Create new … button in the top-right corner of a filtered list (see screenshot)? In my case, filtered events, it doesn’t make much sense to create a past event.
I know about document actions, initial value templates and menu items. They all seem to either extend or bypass this specific create action. Happy for any pointers on how to hide this button!

Here’s the relevant part of the desk structure:

      S.listItem()
        .title("Events")
        .schemaType("event")
        .child(
          S.list()
            .title("Event Lists")
            .items([
              S.listItem()
                .title("Upcoming events")
                .child(
                  S.documentTypeList("event")
                    .title("Upcoming events")
                    .filter("date >= now()")
                ),
              S.listItem()
                .title("Past events")
                .child(
                  S.documentList()
                    .title("Past events")
                    .filter("_type == 'event' && date < now()")
                ),
              S.listItem()
                .title("All events")
                .child(S.documentTypeList("event").title("Events")),
            ])
        ),
      S.divider(),
AI Update

Good news! While there's no built-in method to hide the "Create new" button in filtered document lists, there's a working solution that other community members have used successfully.

The issue you're facing is common – when you create filtered lists (like "Past events"), the create button still appears even though creating a document in that context doesn't make sense. Unfortunately, the Structure Builder API doesn't expose a direct way to hide this button through configuration.

However, there's a clever workaround that involves overriding the serializer of the document list. A community member shared this solution that works by filtering out the create menu item from the serialized configuration:

// customDocumentList.js
export const customDocumentList = (spec, context) => {
  const _documentList = spec || context.documentList()
  
  return {
    ..._documentList,
    __serialize: (...args) => {
      const { menuItems, ...rest } = _documentList.__serialize(...args)
      // Filter out the create menu item
      const filteredMenuItems = menuItems.filter(
        ({ intent }) => intent?.type !== 'create'
      )
      return { menuItems: filteredMenuItems, ...rest }
    }
  }
}

Then use it in your structure:

import { customDocumentList } from './customDocumentList'

S.listItem()
  .title("Past events")
  .child(
    customDocumentList(
      S.documentList()
        .title("Past events")
        .filter("_type == 'event' && date < now()")
    )
  )

Important notes:

  1. This is technically a "hack" since it relies on internal implementation details (__serialize)
  2. Add the optional chaining operator (intent?.type) to handle menu items that don't have an intent property
  3. Be aware this could break if Sanity changes the internal structure in future updates
  4. Consider adding a check for createMenuIdx === -1 if you want to handle cases where there's no create menu

Alternative approaches you might consider:

  • Use document actions to hide/disable the create action based on context
  • Configure initial value templates with initialValueTemplates([]) to remove all template options (though this affects the entire list, not just the button)
  • Use new document options to control which document types appear in global create menus

The serializer override is currently the most targeted solution for hiding the create button on specific filtered lists while keeping it available elsewhere in your structure. Just keep in mind it's working with internal APIs that aren't officially documented, so treat it as a temporary workaround until Sanity potentially adds official support for this use case.

Show original thread
9 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?