Contentstack LogoContentstack Logo

JSON RTE Plugin for Contentstack App SDK

This document outlines how to build JSON Rich Text Editor (RTE) plugins using the Contentstack App SDK. These plugins extend the editor’s functionality by enabling custom formatting, embeds, and interactions within the JSON RTE.

Prerequisites

Structure of JSON RTE

{
  "type": "doc",
  "children": [
    {
      "type": "p",
      "children": [
        {
          "text": "Paragraph"
        }
      ]
    },
    {
      "type": "h1",
      "children": [
        {
          "text": "Heading One"
        }
      ]
    }
  ]
}

Node Types

In the JSON RTE, the JSON structure represents as a Node, which consists of two types:

  • Block Node: Contains a children array with nested nodes.
  • Leaf Node: Contains only a text property and optional formatting marks like bold, italic, etc.

The root of the document is a special Block Node of type doc. All editor content is nested within this root node.

Marks

Marks define text formatting in leaf nodes. Common marks include bold, italic, and underline.

Example:

{
  "text": "I am Bold",
  "bold": true
}

In the above example, bold is the mark applied to the string "I am Bold".

JSON RTE marks example

Render Type

A Block node can be rendered in three ways:

  • Block: Rendered as a standalone block element (e.g., paragraph, heading).
  • Inline: Rendered within other text flows (e.g., links).
  • Void: Represents self-contained, non-editable elements (e.g., images, embeds).

JSON RTE render type example

Path

A Path is an array of indexes that locates a node’s exact position within the document tree.

JSON RTE path example

In the JSON RTE, a path is represented as: Number[]

Examples:

  • The doc node has a path of [0].
  • The first paragraph inside the doc node has a path of [0, 0].

Point

A Point represents a specific location within a leaf node’s text.

It consists of:

  • path: Identifies the node’s position in the document tree.
  • offset: Indicates the character index within the node’s text string.

JSON RTE point example

Structure:

{
  path: Path,
  offset: Number
}

Range

A Range defines a selection within a JSON document using two points:

  • anchor: The starting point of the selection.
  • focus: The ending point of the selection.

Structure:

{
  anchor: Point,
  focus: Point
}

Location

A Location identifies a specific position within the JSON RTE document. It can be one of the following:

  • Path: Specifies a node’s position in the document tree.
  • Point: Specifies a character offset within a leaf node.
  • Range: Specifies a selection spanning from an anchor to a focus point.

Use a Location object as input when targeting or modifying content in the editor.

Inclusion in your project

To build a JSON RTE plugin:

  1. Install the SDK

    Add the @contentstack/app-sdk package to your React project:

    npm install @contentstack/app-sdk
  2. Clone the Boilerplate

    Use the JSON RTE plugin boilerplate from GitHub as a starting point. It includes the required project structure and configuration.

Classes

RTEPlugin(plugin_id, callback)

The RTEPlugin method allows you to create a JSON RTE plugin instance.

Kind: Instance property of the JSON RTE plugin

Returns: Plugin Object

ParameterTypeDescription
plugin_idstringUnique ID for the plugin.
configCallback(rte: IRteParam) => IConfigThis function receives an RTE instance as an argument, and it expects you to return a config object that includes details like title, icon, render, etc.

configCallback: (rte: IRteParam) => IConfig

The IConfig object is a user-defined object that contains metadata that controls how the plugin behaves and appears in the editor.

The following table contains the possible properties of IConfig:

KeyTypeDescription
titlestringToolbar label for the plugin.
iconReactNodeIcon component used for the plugin button.
display('toolbar' | 'hoveringToolbar')[]Location of the plugin
elementType('inline' | 'void' | 'block')[]Render type
renderReactNodeComponent to be rendered within the editor when corresponding plugin_uid appears in json.

RTE Instance (rte)

The rte object provides access to essential functions and properties for interacting with the JSON RTE.

The following is a list of properties and methods of the JSON RTE instance.

Properties:

rte.ref

The rte.ref property returns the HTML reference of the JSON RTE.

rte.fieldConfig

The rte.fieldConfig() property provides metadata about the JSON RTE field, as defined in the content type builder page.

KeyTypeDescription
rich_text_type'basic' | 'advance' | 'custom'Type of JSON RTE selected.
reference_tostring[]UIDs of content types referenced in the JSON RTE.
optionsstring[]Array of selected toolbar buttons (available if rich_text_type is ‘custom’).
titlestringTitle of the RTE field.
uidstringUnique ID for the field
rte.getConfig: () => Object

The rte.getConfig() method retrieves the configuration object defined during plugin creation or selection.

Use this method to access custom plugin parameters, such as API keys or UI settings specified in:

  • The plugin’s initialization logic.
  • Field-level configuration in the builder.

RTE Methods

Access RTE methods using the rte.methodName() syntax. These methods allow you to retrieve paths, modify nodes, apply text formatting, and manage content within the editor.

MethodDescriptionType
getPathRetrieves the path of the node(node: Node) => Path
setAttrsSets attributes for the node (e.g., href for links, src for images).(attrs: Object, options: Option) => void Option: NodeOptions
isNodeOfTypeChecks if the node at the current selection matches the specified type.(type: string) => boolean
getNodeRetrieves the node at the specified location(location: Location) => Node
getNodes

Retrieves a generator of nodes that include the specified location in the options.

By default, it returns nodes at the current selection.

(options: Option) => Node[]

Option: NodeOptions

stringString value of JSON in the given path(location: Location) => string
addMarkAdds formatting (e.g., bold, italic) to selected text.(key: string, val: any) => void
removeMarkRemoves a formatting mark from the selected text.(key: string) => void
hasMarkChecks if the selected text has a mark.(key: string) => boolean
insertTextInserts text at a specified location(text: string, location: Location) => void
getTextRetrieves text from the specified node location.() => string
deleteTextDeletes text from the selected range.() => void
updateNodeUpdates nodes based on specified options.(type: string, attrs: Object, options: Option) => void Option: NodeOptions
unsetNodeConverts a node to a normal paragraph based on specified options(options: Option) => void Option: NodeOptions
insertNode

Inserts a node at a specified location.

Optional select: true selects the node after insertion.

(node: Node, options?: Option) => void Option: NodeOptions & { select?: boolean }
deleteNodeRemoves a node from a specified location.(options: Option) => void Option: { at?: Location, distance?: number, unit?: 'character' | 'word' | 'line' | 'block' }
wrapNodeWraps a node using the provided options and the specified wrapper node.(node: Node, options: Option) => void Option: NodeOptions
unWrapNodeUnwraps a node from its parent using the specified options.(options: Option) => void Option: NodeOptions
mergeNodesMerges nodes based on provided options.(options: Option) => void Option: NodeOptions
getEmbeddedItemsGets details of embedded items JSON RTE.() => Object
getVariableRetrieves a local variable.(name: string) => any
setVariableSets a local variable.(name: string, val: any) => void

RTE Selection Methods (rte.selection)

The rte.selection object provides methods and hooks to manage and query the current selection within the editor.

FunctionDescriptionType
getRetrieves the current selection.() => Range
setSets the selection to the specified location.(location: Location) => void
isSelectedA React hook that returns true when the current node is selected.() => boolean
isFocusedA React hook that returns true when the current node is focused.() => boolean
getEndRetrieves the end location of the editor.() => Path
beforeRetrieves the location before the current selection.(location: Location, options: Option) => Location Option: { distance?: number, unit?: 'offset' | 'character' | 'word' | 'line' | 'block' }
afterRetrieves the location after the current selection.(location: Location, options: Option) => Location Option: { distance?: number, unit?: 'offset' | 'character' | 'word' | 'line' | 'block' }
isPointEqualChecks if two Point objects are equal(point1: Point, point2: Point) => boolean

Node Options:

Functions that transform or modify content accept an options parameter. This parameter includes settings that control where and how the transformation is applied using the NodeOptions interface.

Available Options:

  • at: Specifies the location in the editor where the transformation should occur. It defaults to the user's current selection.
  • match: A custom function that filters which nodes should be affected, based on their content and path.
interface NodeOptions {
  at?: Location;
  match?: (node: Node, path: Location) => boolean;
}

Events function:

The Events functions are built-in methods available on the RTE instance and can be invoked using the syntax: rte.{event_name}().

FunctionDescriptionArguments
isFocusedReturns true if the editor is currently focused.() => boolean
focusSets focus to the editor.() => boolean
blurRemoves focus from the editor.() => boolean

Plugin

Plugin instances expose methods to handle editor events and organize related plugins into dropdowns.

Editor Events

Plugin.on: (event_type, callback) => void
event_typeDescriptionCallback Arguments
keydownTriggered when a key is pressed.({ event: KeyboardEvent, rte: RTE }) => void
execTriggered when a plugin button is clicked.(rte: RTE) => void
deleteBackwardTriggered on backward deletion (e.g., backspace).({ rte: RTE, preventDefault: Function, ...args: [unit: "character" | "word" | "line" | "block"] }) => void
deleteForwardTriggered on forward deletion.({ rte: RTE, preventDefault: Function, ...args: [unit: "character" | "word" | "line" | "block"] }) => void
normalizeCleans up invalid or unwanted node structures.({ rte: RTE, preventDefault: Function, ...args: [[node: Node, path: Path]] }) => void
insertTextInserts text at the current selection.({ rte: RTE, preventDefault: Function, ...args: [string] }) => void
changeFires when any change occurs in the editor.({ rte: RTE, preventDefault: Function }) => void
insertBreakTriggered when the Enter key is pressed.({ rte: RTE, preventDefault: Function }) => void

Dropdown plugin

The Plugin.addPlugins method groups related plugins into a single dropdown menu within the RTE toolbar.

Plugin.addPlugins: (...Plugin) => void

Example:

const ChooseAsset = RTE("choose-asset", () => {
  /** Choose Asset Code */
});
const UploadAsset = RTE("upload-asset", () => {
  /** Upload Asset Code */
});
const Asset = RTE("asset-picker", () => {
  /** Asset Picker Code */
});
Asset.addPlugins(ChooseAsset, UploadAsset);

This groups the ChooseAsset and UploadAsset plugins under a dropdown button represented by Asset.

RTE dropdown plugin example

Was this article helpful?
^