Colored background sections in block content without nested arrays

8 replies
Last updated: Mar 18, 2021
Hi! I need help modelling something with block content. The use case is that I want a section of block content to have a coloured background. My first thought was to add a new "block content" type inside the block content, but multidimensional arrays are not supported. I could add a reference, but I'm concerned about the editing experience. Any other ideas?
AI Update

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 thread
8 replies
That feels more like its an excerpt / explainer as it’s a structured piece of content. Have you considered doing something like you would for a youtube embed? https://www.sanity.io/guides/portable-text-how-to-add-a-custom-youtube-embed-block
Yeah, I managed to do it using something like that. Instead of using type "array" directly inside of the "array", I used a type "object" in between:
{
  name: "colored-section",
  title: "Colored section",
  type: "object",
  fields: [
    {
      ...createBlockContentSchema(sanityBasicBlockContentPreset),
      name: "content",
      title: "Content",
    },
  ],
}
It's a little ugly because it's an object with only one field, but it works. The only thing is that it doesn't render as rich text, so I might have to create a custom renderer there. It would be nice if Sanity had the concept of block "wrappers", so having an element "wrap" a piece of rich text.
Sound like what you’d like is a wrapper of multiple blocks?
As you’d have a title block, then paragraph block.
You’d need a custom renderer for what you are doing as you’ll need to fetch the block and then render the portableText in your output.
I used to work on a block editor and with too many levels and customisation it starts getting complicated.
I used to work on a block editor and with too many levels and customisation it starts getting complicated.
Yeah, I can imagine, once you get really deep you have to do a lot of customization. However, let's not forget that Sanity is giving you this flexibility in the first place, which I love.

Sound like what you’d like is a wrapper of multiple blocks?
Yes, exactly, and as far as I can see right now I'd need a custom renderer. Too bad, but I'll have to look into that
Thanks User!

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.

Was this answer helpful?