
Grab your gear: The official Sanity swag store
Read Grab your gear: The official Sanity swag storeYou're running into a common challenge with Portable Text! You're right that multidimensional arrays aren't supported, but there are several good approaches to achieve colored background sections without compromising the editing experience.
The most elegant approach is to create a custom block object that contains its own Portable Text field plus styling options. This gives you a great editing experience while maintaining the structure:
defineField({
name: 'coloredSection',
type: 'object',
title: 'Colored Section',
fields: [
{
name: 'backgroundColor',
type: 'string',
title: 'Background Color',
options: {
list: [
{title: 'Light Blue', value: 'bg-blue-100'},
{title: 'Light Gray', value: 'bg-gray-100'},
{title: 'Yellow', value: 'bg-yellow-50'}
]
}
},
{
name: 'content',
type: 'array',
title: 'Content',
of: [{type: 'block'}]
}
]
})Then add it to your main Portable Text field:
defineField({
name: 'body',
type: 'array',
of: [
{type: 'block'},
{type: 'coloredSection'}
]
})This approach works great because:
If you need to highlight shorter text spans rather than full sections, you could use custom annotations. These work for inline text styling:
{
name: 'highlight',
type: 'object',
title: 'Highlight',
fields: [
{
name: 'color',
type: 'string',
options: {
list: ['yellow', 'blue', 'green']
}
}
]
}However, annotations are for inline text spans, not block-level sections, so they're better suited for highlighting phrases rather than full paragraphs with backgrounds.
You're right to be concerned about references here. While they work technically, they create a poor editing experience because:
As mentioned in the custom blocks documentation, use objects when content is specific to a single document and doesn't need reuse across multiple documents.
On the frontend using @portabletext/react, you'd render it like this:
const components = {
types: {
coloredSection: ({value}) => (
<div className={value.backgroundColor}>
<PortableText value={value.content} />
</div>
)
}
}
<PortableText value={body} components={components} />This gives you complete control over the HTML structure and styling while keeping the editing experience smooth. The custom block approach is the standard pattern for this exact use case and avoids the nested array limitation entirely!
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