Setting the file. One moment.
Skill 05 · Sanity Best Practices
Subchapter 5.21
references/studio-structure.mdMarkdown4 KBView on GitHub
Custom structure is defined in sanity.config.ts using the structureTool.
import { structureTool } from 'sanity/structure'
import { structure } from './src/structure'
export default defineConfig({
// ...
plugins: [
structureTool({ structure })
]
})Location: src/structure/index.ts
Use a function that receives S (StructureBuilder).
import type { StructureResolver } from 'sanity/structure'
export const structure: StructureResolver = (S) =>
S.list()
.title('Content')
.items([
// ... items
])S.divider() to visually separate logical groups.documentTypeList items to avoid duplication.Singletons are enforced via Structure, NOT schema options. There is no singleton: true schema option.
This is the main case where explicit document IDs are appropriate. For ordinary content documents, let Sanity generate _id values and use references or GROQ lookups to connect records.
S.document().documentId('fixed-id') to lock the document to a specific ID.// Helper to create singleton list items
function createSingleton(S: StructureBuilder, typeName: string, title: string, icon?: ComponentType) {
return S.listItem()
.title(title)
.icon(icon)
.child(
S.document()
.schemaType(typeName)
.documentId(typeName) // Fixed ID = singleton
.title(title)
)
}
// Usage
createSingleton(S, 'settings', 'Site Settings', CogIcon)// By fixed ID (most efficient)
*[_id == "settings"][0]
// By type (works but slower)
*[_type == "settings"][0]For localized singletons (e.g., homepage per language), see localization.md Section 6.
// Define singleton types to exclude from generic lists
const SINGLETONS = ['settings', 'homePage']
export const structure: StructureResolver = (S) =>
S.list()
.title('Website Content')
.items([
// 1. Singletons
S.listItem()
.title('Site Settings')
.icon(CogIcon)
.child(S.document().schemaType('settings').documentId('settings')),
S.divider(),
// 2. Content Verticals
S.listItem()
.title('Blog')
.child(
S.list()
.title('Blog Content')
.items([
S.documentTypeListItem('post').title('Posts'),
S.documentTypeListItem('author').title('Authors'),
])
),
S.divider(),
// 3. Remaining Documents (Filtered)
...S.documentTypeListItems().filter(
(listItem) => !SINGLETONS.includes(listItem.getId() as string)
)
])Add “Web Preview” or other views to documents.
export const defaultDocumentNode: DefaultDocumentNodeResolver = (S, { schemaType }) => {
switch (schemaType) {
case `post`:
return S.document().views([
S.view.form(), // Default form
S.view.component(PreviewComponent).title('Preview') // Custom view
])
default:
return S.document().views([S.view.form()])
}
}