Visual Editing

Custom overlay components and controls

Visual editing overlays can be extended with custom React components for direct in-app content editing and displaying content metadata.

Experimental feature

Custom overlay components allow you to extend the functionality of visual editing overlays with custom React components. Such components can greatly enhance the editing experience by enabling direct, in-app, content editing and displaying rich metadata or controls to content editors.

With custom overlays, you can:

  • Add interactive controls such as color pickers or sliders to configure complex objects (e.g., 3D models).
  • Display additional context, such as related product data from external systems.

You can also customize the Presentation tool's preview header, giving you the flexibility to toggle custom overlays, or add controls, status indicators, or other UI elements that enhance the editor experience.

Prerequisites

Before getting started, ensure the following:

  • Visual Editing enabled with up-to-date dependencies in your front end
  • Sanity Studio v3.65.0 or later (npm install sanity@latest)

Gotcha

Custom overlay plugins

Overlay plugins will let you mount custom React components in the visual editing overlay, enabling complex views and controls for interacting with your content:

All the available configuration properties can be found in the reference section at the end of this article.

Gotcha

Using custom overlay plugins

Depending on your framework and implementation, an array of instantiated plugins should be passed via the plugins property of the object passed to the enableVisualEditing function, or the plugins prop of the <VisualEditing> component. For example:

Overlay plugin types

There are two different types of overlay plugins which define how the plugin behaves in the visual editing overlay:

HUD

Plugins with the hud type can be used to mount a component under the overlay element when hovered.

Exclusive

Plugins with the exclusive type can be used to take exclusive control of the overlay experience. The type of plugin is listed in an overlay element menu. When selected by the user, the component is rendered inside the overlay element and all other overlay UI is hidden until closed.

User-configurable options

Plugins support user-configurable options enabling them to be used multiple times per project with different configurations or shared between multiple projects:

Guarded overlay plugins

The guard conditional function can be used to filter which overlay element the plugin should be displayed on.

When using defineOverlayPlugin, guards can be configured at both the plugin level and the user level. Both must return true for the plugin to be shown, and if no guard function is provided it will pass (as if true):

Updating document data

Custom overlay plugins enable powerful editing capabilities directly in your application, from basic string manipulation to advanced controls for complex content types.

Protip

Install the @sanity/mutate package in your front-end project to create the necessary patches for updating data. Refer to that package’s documentation for available methods.

npm install @sanity/mutate

The example below illustrates how to mount a button in an overlay which appends an exclamation mark at the end of a string value when clicked:

Custom preview header state

The visual editing package exports an named useSharedState hook which given the unique key defined in a custom preview header, will return the value shared by the corresponding useSharedState Presentation tool hook.

Below, we have a HUD plugin that will only be displayed if the showMyPlugin shared state value is set to true from a custom preview header:

Closing exclusive plugins

By default, exclusive plugins can be closed by clicking outside of the overlay element area. However, the closeExclusiveView function can be used to programmatically close the plugin:

Custom component resolver

While the plugin system is helpful at managing the position and visibility of components, the custom component resolver can be used for cases where more control over rendering is required.

Resolvers determine which custom components to mount for specific overlays. Use the defineOverlayComponents helper to conditionally resolve components based on overlay context.

This function runs each time an overlay renders, and the context object it receives can be used to determine which components to return.

Resolver functions can return:

  • JSX elements.
  • React component(s), single or array.
  • Object(s) with component and props values. Use the defineOverlayComponent for convenience and type safety, single or array.
  • undefined or void when no custom components should be mounted.

When using the custom component resolver, the PointerEvents component must be used if interaction is required, this is available through the overlay component props:

Below is an example for how to resolve different custom overlay components conditionally:

Depending on your framework and implementation, the resolver function should be passed via the components property of the object passed to the enableVisualEditing function, or the components prop of the <VisualEditing> component. For example:

Reference

defineOverlayPlugin

  • defineOverlayPlugin<UserOptions>(pluginDefinitionFn): OverlayPluginUserFn

    This utility types a plugin definition function with optional user-configurable options. It also adds an additional user-configurable guard function.

    Returns a function that can be used to create a plugin instance.

    Parameters

    • pluginDefinitionFn(userOptions) => OverlayPluginDefinition

      A function which returns an OverlayPluginDefinition. Which is provided with user-configurable options.

OverlayPluginDefinition

  • Requiredtype'exclusive' | 'hud'
  • Requirednamestring

    Unique identifier for the plugin.

  • RequiredcomponentReactComponent

    A custom React component to render on the overlay. OverlayComponentResolverContext properties are available as props. Exclusive-type plugins also get the closeExclusiveView function prop.

  • titlestring

    Human readable title for the plugin. Shown in the overlay element menu.

  • iconReactComponent

    Icon for the plugin. Shown in the overlay element menu.

  • guard(context: OverlayComponentResolverContext) => boolean

    A guard function which determines whether the plugin should be shown given a resolver context.

OverlayComponentResolverContext

This context object is provided as props to the React overlay components. It is also available on plugin guards and custom component resolvers.

  • documentDocumentSchema

    The resolved field's document schema type.

  • elementElementNode

    The DOM element node that the overlay is visually attached to.

  • targetElementElementNode

    The DOM element node that the Sanity node data was detected on (data attribute or stega-encoded content).

  • fieldOverlayElementField

    The resolved field schema type.

  • focusedboolean

    Whether the overlay is focused or not.

  • nodeSanityNode

    The sanity node data that triggered the overlay (data attribute or stega-encoded content).

  • parentOverlayElementParent

    The resolved field's parent schema type.

  • typestring

    A convenience property, equal to field.value.type.

useDocument

  • useDocuments(): { getDocument, mutateDocument }

    The useDocuments hook can be used in overlay components to access and update the documents currently in use on a page.

  • getDocument(documentId): { id, get, patch, commit }

    Returns an optimistic document interface with the following methods:

    • id: string - The document ID.
    • get: (path?: string): SanityDocument | PathValue - Returns the document snapshot or the specific value at the given path.
    • patch: (patches: OptimisticDocumentPatches, options?: {commit?: boolean | {debounce: number}}) => void - Applies patches to the document, will commit patches by default.
    • commit: () => void - Commits pending changes.

    Parameters

    • documentIdstring

      The ID of the document to get.

  • mutateDocument(documentID, mutations, options): void

    Parameters

    • documentIDstring
      • The ID of the document to mutate.
    • mutationsMutation[]

      The mutations to apply to the document.

    • options{ commit: boolean | {debounce: number }}

      Optional commit options.

useSharedState

  • useSharedState(key, value): Your serializeable state

    The useSharedState enables you to share state between the Presentation tool and your custom overlay components in your front end’s preview.

    Parameters

    • keystring

      Acts as a unique identifier for the shared state within the context. This key is used to associate a specific state value with a logical “slot” in the shared state object.

      Best practice:

      • Use descriptive and unique keys to avoid conflicts between multiple shared states.
      • Keys should be stable (i.e., not dynamically generated) to ensure predictable behavior.

      Example: useSharedState('highlighting', true);

    • valueA serializeable state

      Represents the state value associated with the given key. This value will be shared with other components that query the state using the same key.

      Requirements: Must be JSON serializable (string, number, boolean, null, arrays, or plain objects) to ensure compatibility with mechanisms like serialization, storage, or sharing across contexts.

      Best practices:

      • Ensure the value is minimal and only includes the necessary data.
      • Avoid passing complex or deeply nested structures to keep the shared state manageable.

Resources

Was this page helpful?