How to create nested posts with ordered sub-posts in Sanity schema?
Looking at your question about creating a blog platform with posts and sub-posts (similar to Buildspace), you have a couple of solid approaches in Sanity. Let me walk you through the most straightforward solution that gives you ordering control and good scalability.
The Array of References Approach (Recommended)
Based on your needs, I'd recommend using an array of references in your Post schema. This is the pattern that works best for your use case because:
- Built-in ordering: Arrays in Sanity are inherently orderable with drag-and-drop functionality, so you get ordering control out of the box
- Scalability: Sub-posts remain independent documents that can be reused or managed separately
- Clean queries: Easy to fetch and traverse with GROQ
Here's how to structure it:
// schemas/post.ts
export default defineType({
name: 'post',
type: 'document',
title: 'Post',
fields: [
defineField({
name: 'title',
type: 'string',
title: 'Title'
}),
defineField({
name: 'subPosts',
type: 'array',
title: 'Sub Posts',
of: [{
type: 'reference',
to: [{type: 'subPost'}]
}]
})
]
})
// schemas/subPost.ts
export default defineType({
name: 'subPost',
type: 'document',
title: 'Sub Post',
fields: [
defineField({
name: 'title',
type: 'string',
title: 'Title'
}),
defineField({
name: 'content',
type: 'array',
of: [{type: 'block'}]
})
]
})Querying the Data
To fetch posts with their ordered sub-posts, use this GROQ query:
*[_type == "post"] {
_id,
title,
subPosts[]-> {
_id,
title,
content
}
}The []-> syntax dereferences the array of references, fetching the full sub-post documents while maintaining the order you set in the Studio.
Why This Works Well
- Ordering: The array maintains order via drag-and-drop in the Studio. Each item has a
_keyproperty that Sanity uses internally to track position - Scalability: Sub-posts are separate documents, so you can:
- Query them independently if needed
- Potentially reuse them across multiple posts (if that makes sense for your use case)
- Keep your data normalized
- Studio UX: You get a clean interface where you can select existing sub-posts or create new ones inline
Alternative: Inline Objects
If sub-posts will never be reused and are truly just components of a parent post, you could use inline objects instead:
defineField({
name: 'subPosts',
type: 'array',
of: [{
type: 'object',
fields: [
{name: 'title', type: 'string'},
{name: 'content', type: 'array', of: [{type: 'block'}]}
]
}]
})This is simpler but less flexibleâsub-posts can't exist independently or be queried separately.
The Pattern in Action
The approach I'm recommending is actually a common Sanity pattern for creating ordered collections. As one Sanity team member explained: "The usual approach is to make an 'order' document that holds an array of references to the document type you want to order."
In your case, the Post document serves as that ordering container for SubPosts. This gives you the best of both worlds: the independence of separate documents with the ordering control of arrays.
Good luck with your blog platform! Feel free to ask if you need help with querying or displaying this structure.
Show original thread10 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.