Customizing Content Workflows with Sanity Studio v3 Structure Builder
Learn how to use the Sanity Studio v3 Structure Builder to move beyond flat lists, create filtered document views, and implement conditional field visibility for a professional editing workflow.
10 Aug 2025, 13:19 UTC

Solving the "Flat List" Problem
By default, Sanity Studio displays every schema type as a top-level list in the sidebar. For small projects, this is sufficient. However, as a project grows, a flat list of twenty different content types becomes a bottleneck for editors. The goal is to transform the Studio from a database explorer into a curated content management interface.
The Structure Builder (available in Sanity Studio v3) allows you to define a custom hierarchy. Instead of just listing types, you can create folders, filtered views (e.g., "Drafts" vs "Published"), and singletons (documents that only ever have one instance, like Site Settings).
Implementing a Custom Desk Structure
To customize the sidebar, you must define a structure function and pass it to the structure property of your Studio configuration. This function uses the S (Structure) object to build a tree of lists and items.
Worked Example: Content-Driven Hierarchy
The following configuration organizes a Studio into a "Content" section for collections and a "Settings" section for a singleton document. This should be placed in a separate file (e.g., structure.ts) and imported into sanity.config.ts.
import { StructureResolver } from 'sanity/structure'
export const myStructure = (S) =>
S.list()
.title('Content Management')
.items([
// 1. A standard list of all 'post' documents
S.documentTypeListItem('post').title('All Blog Posts'),
// 2. A filtered list showing only scheduled posts
S.listItem()
.title('Scheduled for Publish')
.child(
S.documentList()
.title('Scheduled Posts')
.filter('_type == "post" && status == "scheduled"')
.order('publishAt asc')
),
S.divider(),
// 3. A Singleton: Direct link to a specific document ID
S.listItem()
.title('Site Settings')
.child(
S.document()
.schemaType('siteSettings')
.documentId('siteSettings')
),
])
Configuration Details
- S.documentTypeListItem: The quickest way to add a schema type to the sidebar.
- S.documentList().filter(): Uses GROQ (Graph-Relational Object Query) to create a dynamic view. This is evaluated server-side, meaning the list updates automatically as documents are edited.
- S.document().documentId(): Forces the Studio to open one specific document rather than a list, effectively creating a singleton.
Refining the Editor Experience with Conditional Visibility
Structure controls how you find content; conditional visibility controls what you see while editing. You can hide fields based on the value of other fields using the hidden property in your schema definition.
Example: Hiding a date picker unless the status is 'scheduled'
export default {
name: 'post',
type: 'document',
fields: [
{
name: 'status',
type: 'string',
options: { list: ['draft', 'published', 'scheduled'] }
},
{
name: 'publishAt',
type: 'datetime',
// The 'parent' argument provides the current document values
hidden: ({ parent }) => parent?.status !== 'scheduled'
}
]
}
Limitations and Common Pitfalls
Performance and Synchronicity
The hidden callback runs on every keystroke in the Studio. It must be a pure synchronous function. Never attempt to perform an async fetch or a heavy computation inside a hidden callback, as this will cause significant input lag for the editor.
The Root Node Requirement
A common error is returning an array of items directly from the structure function. The Structure Builder expects a single root node—typically an S.list(). If you return an array, the Studio sidebar will fail to render.
Singleton Management
While S.document().documentId('id') handles the UI side of a singleton, it does not prevent editors from creating duplicate "Site Settings" documents via the API or other views. To fully enforce a singleton, use the __experimental_singleton: true property in the schema (v3.30+) to hide the type from the global search and other lists.
Verification and Deployment
To verify your structure is working as intended:
- Run
sanity dev. Structure changes hot-reload instantly. - Open the Browser DevTools Network tab. When clicking a filtered list (like "Scheduled Posts"), look for a request to
/data/query. This confirms the GROQ filter is executing on the server. - Test the
hiddenlogic by toggling the status field; thepublishAtfield should appear and disappear without a page refresh.
Once verified, run sanity deploy to push the updated Studio bundle to the Sanity CDN for your production team.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.