Colored background sections in block content without nested arrays
You'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.
Best Solution: Custom Block with Nested Portable Text
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:
- No nested arrays - the colored section is an object, not an array
- Inline editing - editors can work directly in the Studio without switching contexts
- Visual clarity - the section appears as a distinct block in the editor
- Flexible - you can add padding, margins, or other styling options
Alternative: Annotations for Inline Highlighting
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.
Why Not References?
You're right to be concerned about references here. While they work technically, they create a poor editing experience because:
- Editors have to navigate away from the main content
- Content feels disconnected
- Harder to understand the document flow
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.
Rendering Your Custom Block
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!
Show original thread8 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.