Sanity logosanity.ioAll Systems Operational© Sanity 2026
Change Site Theme
Sanity logo

Documentation

    • Overview
    • Platform introduction
    • Next.js quickstart
    • Nuxt.js quickstart
    • Astro quickstart
    • React Router quickstart
    • Studio quickstart
    • Build with AI
    • Content Lake
    • Functions
    • APIs and SDKs
    • Agent Actions
    • Visual Editing
    • Blueprints
    • Platform management
    • Dashboard
    • Studio
    • Canvas
    • Media Library
    • App SDK
    • Content Agent
    • HTTP API
    • CLI
    • Libraries
    • Specifications
    • Changelog
    • User guides
    • Developer guides
    • Courses and certifications
    • Join the community
    • Templates
Studio
Overview

  • Setup and development

    Installation
    Project Structure
    Development
    Hosting and deployment
    Embedding Sanity Studio
    Upgrading Sanity Studio
    Environment Variables
    Using TypeScript in Sanity Studio
    Understanding the latest version of Sanity

  • Configuration

    Introduction
    Workspaces
    Schema and forms
    Conditional fields
    Field Groups
    List Previews
    Connected Content
    Validation
    Initial Value Templates
    Cross Dataset References
    Sort Orders
    Visual editing and preview
    Incoming reference decoration

  • Block Content (Portable Text)

    Introduction
    Configure the Portable Text Editor
    Customize the Portable Text Editor
    Create a Portable Text behavior plugin
    Add Portable Text Editor plugins to Studio
    Common patterns
    Standalone Portable Text Editor

  • Studio customization

    Introduction
    Custom component for Sanity Studio
    Custom authentication
    Custom asset sources
    Diff components
    Form Components
    How form paths work
    Icons
    Favicons
    Localizing Sanity Studio
    New Document Options
    Studio Components
    Studio search configuration
    Focus and UI state in custom inputs
    Real-time safe patches for input components
    Sanity UI
    Studio Tools
    Create a custom Studio tool
    Tools cheat sheet
    Theming

  • Workflows

    The Dashboard tool for Sanity Studio
    Add widgets to dashboard
    Document actions
    Release Actions
    Custom document badges
    Localization
    Content Releases Configuration
    Enable and configure Comments
    Configuring Tasks
    Scheduled drafts
    Scheduled publishing (deprecated)
    Manage notifications

  • Structure builder

    Introduction
    Get started with Structure Builder API
    Override default list views
    Create a link to a single edit page in your main document type list
    Manually group items in a pane
    Dynamically group list items with a GROQ filter
    Create custom document views with Structure Builder
    Cheat sheet
    Structure tool
    Reference

  • Plugins

    Introduction
    Installing and configuring plugins
    Developing plugins
    Publishing plugins
    Internationalizing plugins
    Reference
    Official plugins repo

  • AI Assist

    Installation
    Translation
    Custom field actions
    Field action patterns

  • User guides

    Comments
    Task
    Copy and paste fields
    Compare document versions
    Content Releases
    Scheduled drafts
    View incoming references
    Common keyboard shortcuts

  • Studio schema reference

    Studio schema configuration
    Array
    Block
    Boolean
    Cross Dataset Reference
    Date
    Datetime
    Document
    File
    Geopoint
    Global Document Reference
    Image
    Number
    Object
    Reference
    Slug
    Span
    String
    Text
    URL

  • Studio reference

    Asset Source
    Configuration
    Document
    Document Badges
    Document Actions
    Form
    Form Components
    Hooks
    Structure tool
    Studio Components Reference
    Tools
    Initial Value Templates
    Studio API reference

On this page

Previous

Create a Portable Text behavior plugin

Next

Common patterns

Was this page helpful?

On this page

  • Add the plugin to your project
  • Option 1: Add the plugin directly
  • Global
  • Local
  • Option 2: Create a container for plugins
  • Option 3: Wrap in a Studio plugin
StudioLast updated October 2, 2025

Add Portable Text Editor plugins to Studio

Use official and community-built Portable Text Editor plugins in your studio.

Studio uses the standalone Portable Text Editor to power the block content editing experience. This makes the editor aware of your schema, and also lets you modify the editor from your existing Sanity config and schema.

Because the Portable Text Editor itself doesn't require Studio, there can be times where you need to include a Portable Text Editor plugin that isn't set up to work with Studio.

This guide shows you how to use an existing Portable Text Editor plugin with your studio.

Prerequisites:

  • sanity version 3.92.0 or higher is required for Studio to apply plugins.

Add the plugin to your project

The first step is to add the Portable Text Editor plugin to your project. For the examples in this guide, we'll use the CharacterPairDecoratorPlugin from the official plugins repository. It lets you define a markdown-style shortcut and link it to a decorator.

npm install @portabletext/plugin-character-pair-decorator
pnpm add @portabletext/plugin-character-pair-decorator

We'll configure the plugin to make any text wrapped in # bold. For example, #example# will receive the "strong" decorator.

Option 1: Add the plugin directly

The most straightforward approach is to import the plugin and add it directly to your Studio config for global use, or your schema for specific component use.

Note the change to TSX

This approach requires that you change your files to TSX to accommodate JSX syntax. It means fewer files, but may not be your preferred technique. See the other approaches below for alternatives.

Global

This causes all block content in your studio to only use the single plugin.

import { defineConfig } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export default defineConfig({
  // ... omitted for brevity
  form: {
    components: {
      portableText: {
        plugins: (props)=>{
          return (
            <>
              {props.renderDefault(props)}
              <CharacterPairDecoratorPlugin 
                decorator={({schema}) =>
                  schema.decorators.find((d) => d.name === 'strong')?.name
                }
                pair={{char: '#', amount: 1}}
              />
            </>
          )
        },
      }
    },
  }
})

Local

import { defineType } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export const post = defineType({
  title: 'Blog post',
  name: 'post',
  type: 'document',
  fields: [
    // ...
    {
      type: 'array',
      name: 'content',
      title: 'Post Body',
      of: [
        {
          type: 'block',
        }
      ],
      components: {
        portableText: {
          plugins: (props)=>{
            return (
              <>
              {props.renderDefault(props)}
              <CharacterPairDecoratorPlugin 
                decorator={({schema}) =>
                  schema.decorators.find((d) => d.name === 'strong')?.name
                }
                pair={{char: '#', amount: 1}}
              />
            </>
            )
          }
        }
      }
    },
    // ...
  ],
})

Option 2: Create a container for plugins

If you don't want to change your schema and configuration files to TSX, you can wrap all editor plugins in a single container component, then use that when needed. For example, in the guide on creating your own Portable Text Editor plugin, we export a PortableTextEditorPlugins function.

import type { PortableTextPluginsProps } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export function PortableTextEditorPlugins(props: PortableTextPluginsProps) {
  return (
    <>
      {props.renderDefault(props)}
      <CharacterPairDecoratorPlugin 
        decorator={({schema}) =>
          schema.decorators.find((d) => d.name === 'strong')?.name
        }
        pair={{char: '#', amount: 1}}
      />
      {/* Add more plugins here  */}
    </>
  )
}

Then use that container globally, in the sanity.config.ts or locally in your schema.

import { defineConfig } from "sanity"
import { PortableTextEditorPlugins } from './plugins/portable-text'

export default defineConfig({
  // ...
  form: {
    components: {
      portableText: {
        plugins: PortableTextEditorPlugins,
      },
    },
  },
  // ...
})
import { defineType } from 'sanity'
import { PortableTextEditorPlugins } from './plugins/portable-text'

export const post = defineType({
  title: 'Blog post',
  name: 'post',
  type: 'document',
  fields: [
    // ...
    {
      type: 'array',
      name: 'content',
      title: 'Post Body',
      of: [
        {
          type: 'block',
        }
      ],
      components: {
        portableText: {
          plugins: PortableTextEditorPlugins,
        }
      }
    },
    // ...
  ],
})

Option 3: Wrap in a Studio plugin

One approach is to wrap the Portable Text Editor plugin in a Sanity Studio plugin using definePlugin, then add it to your config file as a plugin.

import { definePlugin } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export const characterPair = definePlugin({
  name: 'characterPair',
  form: {
    components: {
      portableText: {
        plugins: (props)=>{
          return (
            <>
              {props.renderDefault(props)}
              <CharacterPairDecoratorPlugin 
                decorator={({schema}) =>
                  schema.decorators.find((d) => d.name === 'strong')?.name
                }
                pair={{char: '#', amount: 1}}
              />
            </>
          )
        },
      }
    },
  }
})
import { defineConfig } from 'sanity'
import { characterPair } from './plugins/characterPair'
export default defineConfig({
  // ... omitted for brevity
  plugins: [
    //...
    characterPair()
  ]
})

If you're packaging a plugin this way for distribution, you should also expose the configuration so it's available from the sanity.config.ts.

npm install @portabletext/plugin-character-pair-decorator
pnpm add @portabletext/plugin-character-pair-decorator
import { defineConfig } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export default defineConfig({
  // ... omitted for brevity
  form: {
    components: {
      portableText: {
        plugins: (props)=>{
          return (
            <>
              {props.renderDefault(props)}
              <CharacterPairDecoratorPlugin 
                decorator={({schema}) =>
                  schema.decorators.find((d) => d.name === 'strong')?.name
                }
                pair={{char: '#', amount: 1}}
              />
            </>
          )
        },
      }
    },
  }
})
import { defineType } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export const post = defineType({
  title: 'Blog post',
  name: 'post',
  type: 'document',
  fields: [
    // ...
    {
      type: 'array',
      name: 'content',
      title: 'Post Body',
      of: [
        {
          type: 'block',
        }
      ],
      components: {
        portableText: {
          plugins: (props)=>{
            return (
              <>
              {props.renderDefault(props)}
              <CharacterPairDecoratorPlugin 
                decorator={({schema}) =>
                  schema.decorators.find((d) => d.name === 'strong')?.name
                }
                pair={{char: '#', amount: 1}}
              />
            </>
            )
          }
        }
      }
    },
    // ...
  ],
})
import type { PortableTextPluginsProps } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export function PortableTextEditorPlugins(props: PortableTextPluginsProps) {
  return (
    <>
      {props.renderDefault(props)}
      <CharacterPairDecoratorPlugin 
        decorator={({schema}) =>
          schema.decorators.find((d) => d.name === 'strong')?.name
        }
        pair={{char: '#', amount: 1}}
      />
      {/* Add more plugins here  */}
    </>
  )
}
import { defineConfig } from "sanity"
import { PortableTextEditorPlugins } from './plugins/portable-text'

export default defineConfig({
  // ...
  form: {
    components: {
      portableText: {
        plugins: PortableTextEditorPlugins,
      },
    },
  },
  // ...
})
import { defineType } from 'sanity'
import { PortableTextEditorPlugins } from './plugins/portable-text'

export const post = defineType({
  title: 'Blog post',
  name: 'post',
  type: 'document',
  fields: [
    // ...
    {
      type: 'array',
      name: 'content',
      title: 'Post Body',
      of: [
        {
          type: 'block',
        }
      ],
      components: {
        portableText: {
          plugins: PortableTextEditorPlugins,
        }
      }
    },
    // ...
  ],
})
import { definePlugin } from 'sanity'
import { CharacterPairDecoratorPlugin } from '@portabletext/plugin-character-pair-decorator'

export const characterPair = definePlugin({
  name: 'characterPair',
  form: {
    components: {
      portableText: {
        plugins: (props)=>{
          return (
            <>
              {props.renderDefault(props)}
              <CharacterPairDecoratorPlugin 
                decorator={({schema}) =>
                  schema.decorators.find((d) => d.name === 'strong')?.name
                }
                pair={{char: '#', amount: 1}}
              />
            </>
          )
        },
      }
    },
  }
})
import { defineConfig } from 'sanity'
import { characterPair } from './plugins/characterPair'
export default defineConfig({
  // ... omitted for brevity
  plugins: [
    //...
    characterPair()
  ]
})