
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeI totally get the frustration - this is a really common pattern people need! The key issue you're running into is that filtered lists in Structure Builder are read-only views. You can't create new documents directly in them because Sanity doesn't know what filter values to pre-populate.
Here's the best approach that gives you both organized browsing AND the ability to create documents:
Create two document types where tags reference their parent category:
// schemas/category.ts
export default {
name: 'category',
type: 'document',
title: 'Category',
fields: [
{
name: 'title',
type: 'string',
title: 'Title',
validation: (Rule) => Rule.required()
},
{
name: 'slug',
type: 'slug',
title: 'Slug',
options: { source: 'title' }
}
]
}
// schemas/tag.ts
export default {
name: 'tag',
type: 'document',
title: 'Tag',
fields: [
{
name: 'title',
type: 'string',
title: 'Title',
validation: (Rule) => Rule.required()
},
{
name: 'category',
type: 'reference',
title: 'Category',
to: [{type: 'category'}],
validation: (Rule) => Rule.required()
}
]
}Now create a Structure Builder configuration that gives you both the hierarchical view and flat creation lists:
// structure/index.ts
import type {StructureResolver} from 'sanity/structure'
export const structure: StructureResolver = (S) =>
S.list()
.title('Content')
.items([
// Hierarchical browsing view
S.listItem()
.title('Tags by Category')
.child(
S.documentTypeList('category')
.title('Categories')
.child((categoryId) =>
S.documentList()
.title('Tags')
.filter('_type == "tag" && category._ref == $categoryId')
.params({ categoryId })
)
),
S.divider(),
// Flat lists for creating new documents
S.documentTypeListItem('category').title('All Categories'),
S.documentTypeListItem('tag').title('All Tags'),
])Wire it up in your config:
// sanity.config.ts
import {defineConfig} from 'sanity'
import {structureTool} from 'sanity/structure'
import {structure} from './structure'
export default defineConfig({
// ...other config
plugins: [
structureTool({structure}),
// ...other plugins
],
// ...schemas
})To create a new category:
To create a new tag:
To browse tags by category:
The "Tags by Category" view uses child resolvers to create a hierarchical browsing experience - perfect for viewing and editing existing content in an organized way. The "All Categories" and "All Tags" lists give you unfiltered access where Sanity can properly handle document creation without needing to guess filter values.
This pattern is exactly how parent-child taxonomies are meant to work in Sanity. You get the best of both worlds: intuitive organization for browsing/editing and straightforward workflows for creating new content! 😊
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.
Content operations
Content backend


The only platform powering content operations
By Industry


Tecovas strengthens their customer connections
Build and Share

Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag store