Creating a Margin Action with Decorator in Sanity Block Editor

6 replies
Last updated: Dec 2, 2025
Hi all! Does anyone know how you'd go about creating a Margin Action in the block editor that applies a decorator to the current block? I'm trying to create a button in the margin that will allow the user to add a "comment" on a block-by-block basis:
AI Update

Creating a Margin Action in Sanity's block editor to apply decorators is a bit tricky because the Portable Text Editor doesn't have a built-in API specifically for "margin actions" that apply decorators to entire blocks. However, I can guide you through the closest approaches to achieve what you're looking for.

Understanding the Challenge

Decorators in Sanity's Portable Text are designed to work on inline text selections, not entire blocks. They're simple string-based formatting marks like bold, italic, or custom styles applied to spans of text. This means you can't directly apply a decorator to a whole block from a margin button in the traditional sense.

Option 1: Use Custom Block Styles (Best for Block-Level Comments)

Instead of decorators, use custom block styles which apply to entire blocks. This is more semantically correct for your use case:

import {defineType} from 'sanity'
import {CommentIcon} from '@sanity/icons'

export default defineType({
  name: 'blockContent',
  type: 'array',
  of: [
    {
      type: 'block',
      styles: [
        {title: 'Normal', value: 'normal'},
        {title: 'Heading 1', value: 'h1'},
        // Your custom comment style
        {
          title: 'Commented',
          value: 'commented',
          icon: CommentIcon
        }
      ]
    }
  ]
})

Users can then select a block and change its style from the style dropdown in the toolbar. While this isn't a margin button, it achieves block-level marking.

Option 2: Use Annotations with Metadata

For actual commenting functionality where you want to store comment text, use annotations instead of decorators. Annotations can carry additional data:

marks: {
  annotations: [
    {
      name: 'comment',
      type: 'object',
      title: 'Comment',
      icon: CommentIcon,
      fields: [
        {
          name: 'text',
          type: 'text',
          title: 'Comment Text'
        },
        {
          name: 'author',
          type: 'string',
          title: 'Author'
        }
      ]
    }
  ]
}

This allows users to highlight text and add comments with metadata, which is more powerful than simple decorators.

Option 3: Custom Block Type with Comment Field

For true block-level commenting, create a wrapper block type:

{
  name: 'commentedBlock',
  type: 'object',
  title: 'Block with Comment',
  fields: [
    {
      name: 'content',
      type: 'block',
      title: 'Content'
    },
    {
      name: 'comment',
      type: 'text',
      title: 'Comment'
    }
  ]
}

Then include this in your block content array:

of: [
  {type: 'block'},
  {type: 'commentedBlock'}
]

Why Margin Actions for Decorators Don't Exist

The Portable Text Editor's architecture separates concerns: decorators operate on text selections (controlled by the user highlighting text), while block-level operations use styles or custom block types. There isn't a built-in "margin action" API that bridges these two concepts because it would be semantically confusing—decorators are meant for inline formatting, not block-level metadata.

If you absolutely need a custom UI button in the margin area, you'd need to create a custom Studio plugin with significant custom React components that hook into the Portable Text Editor's internal APIs, which is quite advanced and not officially documented for this specific use case.

Recommendation

For a commenting feature on blocks, I'd strongly recommend Option 2 (annotations) if you want inline comments, or Option 1 (custom block styles) if you just need to visually mark entire blocks as "commented" without storing additional data. Both work with the editor's existing UI patterns and don't require custom margin buttons.

Show original thread
6 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.

Was this answer helpful?