Declaring Nested Lists the Block Type for PortableText
Looking at your question about declaring nested lists (ul and ol) in PortableText, I can help clarify how lists work in Sanity's block type schema.
The key thing to understand: Lists in PortableText are actually configured through the block type itself, not as separate types. By default, the block type in Sanity already supports lists - you don't need to declare them separately.
Here's how to configure lists in your schema:
defineField({
name: 'content',
type: 'array',
of: [
{
type: 'block',
// Configure which list types are available
lists: [
{title: 'Bullet', value: 'bullet'},
{title: 'Numbered', value: 'number'}
],
// Configure list item levels (for nesting)
styles: [
{title: 'Normal', value: 'normal'},
{title: 'H1', value: 'h1'},
// ... other styles
],
marks: {
// ... your marks configuration
}
}
]
})Important points about rendering:
- Use the
_rawfield in queries: When querying PortableText content (especially with GraphQL), you need to query the_rawversion of the field for lists to render properly. Without this, list items may appear as regular paragraphs. This is the most common issue people encounter.
query {
post {
_rawContent // Use _raw prefix
}
}- Rendering with @portabletext/react: When customizing list rendering, you target the list type (
bulletornumber), notulorli:
import {PortableText} from '@portabletext/react'
const components = {
list: {
bullet: ({children}) => <ul className="custom-bullet-list">{children}</ul>,
number: ({children}) => <ol className="custom-numbered-list">{children}</ol>
},
listItem: {
bullet: ({children}) => <li className="custom-bullet-item">{children}</li>,
number: ({children}) => <li className="custom-number-item">{children}</li>
}
}
<PortableText value={content} components={components} />For nested lists: Sanity's block editor supports nested lists out of the box. Users can indent list items in the editor using Tab/Shift+Tab, and the nesting structure is automatically preserved in the PortableText data.
The confusion often comes from the fact that lists in PortableText are stored as blocks with listItem and level properties rather than as nested HTML-like structures. The rendering libraries handle converting this flat structure into proper nested HTML automatically.
If you're still seeing issues with lists not rendering correctly, make sure you're:
- Querying the
_rawfield (this is usually the culprit!) - Using a proper PortableText renderer like @portabletext/react
- Not accidentally filtering out blocks with
listItemproperties in your query
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.