137 lines
3.4 KiB
Markdown
137 lines
3.4 KiB
Markdown
---
|
|
title: "Sanity Studio Structure Rules"
|
|
description: Rules for customizing the Sanity Studio Structure (S.structure).
|
|
---
|
|
|
|
# Sanity Studio Structure Rules
|
|
|
|
## 1. Setup
|
|
Custom structure is defined in `sanity.config.ts` using the `structureTool`.
|
|
|
|
```typescript
|
|
import { structureTool } from 'sanity/structure'
|
|
import { structure } from './src/structure'
|
|
|
|
export default defineConfig({
|
|
// ...
|
|
plugins: [
|
|
structureTool({ structure })
|
|
]
|
|
})
|
|
```
|
|
|
|
## 2. Structure Definition
|
|
**Location:** `src/structure/index.ts`
|
|
|
|
Use a function that receives `S` (StructureBuilder).
|
|
|
|
```typescript
|
|
import type { StructureResolver } from 'sanity/structure'
|
|
|
|
export const structure: StructureResolver = (S) =>
|
|
S.list()
|
|
.title('Content')
|
|
.items([
|
|
// ... items
|
|
])
|
|
```
|
|
|
|
## 3. Organization Principles
|
|
1. **Singletons First:** Place critical site-wide settings (Global Settings, Homepage) at the top.
|
|
2. **Dividers:** Use `S.divider()` to visually separate logical groups.
|
|
3. **Filtered Lists:** Always exclude Singleton documents from generic `documentTypeList` items to avoid duplication.
|
|
|
|
## 4. Singleton Pattern (Critical)
|
|
|
|
**Singletons are enforced via Structure, NOT schema options.** There is no `singleton: true` schema option.
|
|
|
|
### How Singletons Work
|
|
1. Use `S.document().documentId('fixed-id')` to lock the document to a specific ID.
|
|
2. Filter the type from generic lists to prevent duplicate entries.
|
|
|
|
### Singleton Helper Function
|
|
```typescript
|
|
// 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)
|
|
```
|
|
|
|
### Querying Singletons
|
|
```groq
|
|
// 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.
|
|
|
|
## 5. Implementation Pattern
|
|
|
|
```typescript
|
|
// 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)
|
|
)
|
|
])
|
|
```
|
|
|
|
## 6. Views (Split Pane)
|
|
Add "Web Preview" or other views to documents.
|
|
|
|
```typescript
|
|
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()])
|
|
}
|
|
}
|
|
```
|