Studio

Create a Portable Text behavior plugin

Use behavior plugins to add custom interactions to the Portable Text block editor in Studio.

You can add custom behaviors to your studio's Portable Text Editor (PTE) by creating custom React components, hooking into the editor's behavior API, registering a plugin, and adding it to your configuration or schema.

Experimental feature

In this guide we'll create a behavior plugin that auto-closes bracket pairs. For example, when users type an opening bracket ({), the editor will automatically add a closing bracket (}) and move the cursor in between them. The focus of this guide is on incorporating PTE behaviors in Studio. Additional details for working with the Behaviors API can be found in the Portable Text Editor documentation.

Prerequisites:

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

Create the custom behavior component

Navigate to your studio's project directory and add the latest version of @portabletext/editor with the package manager of your choice. For example:

PTE Plugins are React components. This allows them to maintain their own state, handle additional logic, and render any components they need.

Create a new component file in the location of your choice. We recommend creating a plugins/pte/ directory or similar. This example names the file auto-close-brackets-plugin.tsx.

Start by importing the dependencies you'll need to create the plugin.

Define the behavior

Next, in the same file, define the behavior using the defineBehavior helper.

All behaviors follow this process:

  • Listen for an event. In the example, on listens for a insert.text event.
  • Use a guard to decide if they should run or not. In the example, guard checks if the last inserted character matches any bracket characters. If it does, it returns the closing character to pass it on to the next step.
  • Trigger a set of actions to perform on the editor. In the example, we first send the original action back to insert the first bracket into the editor. Then we send a pair of actions to insert the closing bracket and move the cursor over one place so it rests between the brackets.

This guide doesn't go much further into the Behaviors API. You can learn more about these concepts in the Portable Text Editor documentation.

Create a React component to register the behavior

Next, in the same file, create a function component to register the new behavior with the PTE.

Aside from some React conventions, this code does one core task: it registers the autoCloseBracketsBehavior from the previous step with any instance of the PTE it is attached to (we'll do this soon). Other behaviors may use this space to perform additional logic like state management.

Finally, to make adding the plugin to your schema and config files easier, create a file to export a function that wraps the plugin (or group of plugins). This is optional, but makes it easier to use them as custom form components without changing your file types to support TSX.

Here's our completed auto-close-brackets-plugin.tsx and plugin index.tsx file:

Integrate the plugin with your studio schema

There are two ways to add this PTE plugin to your studio. Globally for all PTE blocks, or locally to specific blocks in your schema.

Globally

To apply the plugin to all PTE instances throughout your studio, you can add it globally by setting it as the pte form component.

In your studio config file, use the Form Components configuration to add the plugin as shown in this example.

Locally

Sometimes you want certain plugins for certain PTE fields. In those instances, customize the component in field for the schema type. For example:

Optional: Composing multiple plugins

The above examples use the PortableTextEditorPlugins function to prepare the behavior for inclusion in Sanity schemas and configuration files. You can also do this in-line in the schema or configuration by converting those files to tsx|jsx files and using the AutoCloseBracketsBehaviorPlugin directly instead. For example:

Include additional plugins as needed, for example:

Additionally, you can use this same approach with the earlier PortableTextEditorPlugins technique to package groups of plugins together.

Was this page helpful?