Documentation
Rich Text Editor

Rich Text Editor

A rich text editor for React to create rich text content.

Introduction

This rich text editor (called as "Lyrical") is heavily built on top of Tiptap (opens in a new tab) including its Free to Pro extensions along with our own custom extensions. It is designed in a way to give as much flexibility as tiptap give us but at the same time make it easy to use and provide some common features out of the box along with styles matching our Design System. It also exposes commonly used toolbar components ready to use!

💡

Since it is heavily built on Tiptap and majority of things are same, it is required to read the Tiptap Docs (opens in a new tab) for understanding the basics of how things work in Tiptap.

Here is a basic example of using the editor:

Dependencies

Usage

Using Editor

In Tiptap we used to create new editor instance by using the useEditor hook from @tiptap/react package. But in our case we expose our own hook called useLyrical which is a wrapper around useEditor hook and provides some extensions out of the box. The best part here is that you got the flexibility of doing anything you used to do with useEditor hook!

See the API Referece of editor instance here.

Here is a comparison of useEditor and useLyrical hooks and it's basic implementation.

import { EditorContent, useEditor } from "@tiptap/react"
 
export const WithTiptap = () => {
  const editor = useEditor()
 
  return <EditorContent editor={editor} />
}
import {
  LyricalContent,
  LyricalProvider,
  useLyrical,
} from "@inspectra/ui/lyrical"
 
export const Withyrico = () => {
  const editor = useLyrical()
 
  return (
    <LyricalProvider editor={editor}>
      <LyricalContent />
    </LyricalProvider>
  )
}

Using the editor instance from useLyrical hook provides some default extensions out of the box. Here is the list of extensions that are included in it:

💡
See full list of available extensions here.

Using createExtension

This is a utility function that we expose to initiate an extension. It accepts the name of the extension as a string and returns the extension for further configuration. It also provides auto suggestion of all available extensions.

By default, the function needs the extension name as the first parameter but it also supports an optional second parameter that allows to set the initial configuration of the extension, as we would do with createExtension("extensionName").configure({...}).

Therefore, we can use it in two ways:

  1. By setting the configuration values inside the second parameter of the function.
  2. By setting the configuration values using the configure method of the extension.

Remember that if we use both cases at the same time, the configuration values of the second parameter of the function will be stepped on by the configuration values of the configure method of the extension.

import {
  LyricalContent,
  LyricalProvider,
  useLyrical,
} from "@inspectra/ui/lyrical"
import { createExtension } from "@inspectra/ui/lyrical/extensions"
 
export const Withyrico = () => {
  const editor = useLyrical({
    extensions: [
      createExtension("color"),
      createExtension("youtube"),
      createExtension("table")
        .configure({
          resizable: true, // <------------- configuration
          expandable: true,
        })
        .extend({
          // <------------- extend
        }),
      createExtension("table-row"),
      createExtension("table-cell"),
      createExtension("table-header"),
    ],
  })
 
  return (
    <LyricalProvider editor={editor}>
      <LyricalContent />
    </LyricalProvider>
  )
}

Example

Besides these core utilities and extensions, we also expose a LyricalProvider which will be used as a React Provider context to share editor instance among its childrens which are coming from lyrical. This opnes the door of using our pre-built toolbar components!

Here is a basic implementation of using the editor with some toolbar components:

💡

It is good to know we are not limited to use only these toolbar components. Since we have access to the editor instance we can create any custom toolbar as our need!

import {
  LyricalContent,
  LyricalProvider,
  useLyrical,
} from "@inspectra/ui/lyrical"
import {
  BoldToolbar,
  HeadingsToolbar,
  ItalicToolbar,
  UnderlineToolbar,
} from "@inspectra/ui/lyrical/components"
import { createExtension } from "@inspectra/ui/lyrical/extensions"
 
export const Withyrico = () => {
  const editor = useLyrical({
    extensions: [
      createExtension("color"),
      createExtension("youtube"),
      createExtension("table").configure({
        resizable: true,
      }),
      createExtension("table-row"),
      createExtension("table-cell"),
      createExtension("table-header"),
    ],
  })
 
  return (
    <LyricalProvider editor={editor}>
 
    // <------------- the toolbar components ------------>
      <HeadingsToolbar />
      <BoldToolbar />
      <ItalicToolbar />
      <UnderlineToolbar />
 
      <LyricalContent />
    </LyricalProvider>
  )
}

Extensions

Extensions are the building blocks of the editor. They are the ones that provide the functionality to the editor.

Here we demonstrate the full list of extensions that are available in lyrical.

ExtensionIncluded by defaultTypeAPI reference
StarterKit * (required)__extensionView API reference (opens in a new tab)
BlockquoteIncludednodeView API reference (opens in a new tab)
BulletListIncludednodeView API reference (opens in a new tab)
CharacterCount__extensionView API reference (opens in a new tab)
Code BlockIncludednodeView API reference (opens in a new tab)
CodeIncludedmarkView API reference (opens in a new tab)
Color__extensionView API reference (opens in a new tab)
TextStyleIncludedmarkView API reference (opens in a new tab)
Details__nodeView API reference (opens in a new tab)
DetailsContent__nodeView API reference (opens in a new tab)
DetailsSummary__nodeView API reference (opens in a new tab)
DropcursorIncludedextensionView API reference (opens in a new tab)
HardBreakIncludednodeView API reference (opens in a new tab)
HighlightIncludedmarkView API reference (opens in a new tab)
HorizontalRuleIncludednodeView API reference (opens in a new tab)
LinkIncludedmarkView API reference (opens in a new tab)
OrderedListIncludednodeView API reference (opens in a new tab)
Placeholder__extensionView API reference (opens in a new tab)
SubscriptIncludedmarkView API reference (opens in a new tab)
SuperscriptIncludedmarkView API reference (opens in a new tab)
Table__nodeView API reference (opens in a new tab)
TableHeader__nodeView API reference (opens in a new tab)
TableRow__nodeView API reference (opens in a new tab)
TableCell__nodeView API reference (opens in a new tab)
TaskItem__nodeView API reference (opens in a new tab)
TaskList__nodeView API reference (opens in a new tab)
TextAlignIncludedextensionView API reference (opens in a new tab)
TypographyIncludedextensionView API reference (opens in a new tab)
UnderlineIncludedmarkView API reference (opens in a new tab)
Youtube__nodeView API reference (opens in a new tab)
Mention__nodeView API reference (opens in a new tab)
InvisibleCharacters__extensionView API reference (opens in a new tab)
Collaboration__extensionView API reference (opens in a new tab)
CollaborationCursor__extensionView API reference (opens in a new tab)
CollaborationHistory__extensionView API reference (opens in a new tab)
GapCursorIncludedextensionView API reference (opens in a new tab)
FontFamily__extensionView API reference (opens in a new tab)
Image__nodeView API reference (opens in a new tab)
Mathematics__extensionView API reference (opens in a new tab)
Emoji__nodeView API reference (opens in a new tab)
SlashCommands__extensionView API reference
AI__extensionView API reference (opens in a new tab)
File Handler__extensionView API reference (opens in a new tab)

How to use an extension?

Using an extension is very easy. We exposed a utility funtion called createExtension which will give auto suggestions of all the available extensions. It accepts the name of the extension as a string and returns the extension for further configuration.

By registering an extension name inside createExtension function, it initiate the extension and make the functionality ready to use! Yet, it is also possible to create completely custom extension and use it in the editor. For the guide of creating new extension, read here

Here is a basic example of using avialable extensions:

import {
  LyricalContent,
  LyricalProvider,
  useLyrical,
} from "@inspectra/ui/lyrical"
import { createExtension } from "@inspectra/ui/lyrical/extensions"
 
export const Withyrico = () => {
  const editor = useLyrical({
    extensions: [
      // <------------- using extensions ------------>
      createExtension("color"),
      createExtension("youtube"),
      createExtension("table").configure({
        resizable: true,
      }),
      createExtension("table-row"),
      createExtension("table-cell"),
      createExtension("table-header"),
    ],
  })
 
  return (
    <LyricalProvider editor={editor}>
      <LyricalContent />
    </LyricalProvider>
  )
}

Creating Custom Extension

Creating a cusotm extension can be difficult. So it is important to understand the basics of how extension work in tiptap. Here is the full documentation of creating custom extension on Tiptap Docs (opens in a new tab).

Apart from it, the extension creating process is same but in our case we don't need to install the tiptap core packages. We already exported all the core packages like @tiptap/core as @inspectra/ui/lyrical/core, @tiptap/pm as @inspectra/ui/lyrical/pm and @tiptap/react as @inspectra/ui/lyrical/react and @tiptap/suggestion as @inspectra/ui/lyrical/suggestion. So we can use directly like this:

import { LyricalCore } from "@inspectra/ui/lyrical/core"
 
export const CustomExtension = LyricalCore.Node.create({
  name: "custom-image",
 
  //   other code goes here
})

This is the basic structure of creating a custom extension. And we can use it just like this:

const editor = useLyrical({
  extensions: [
    CustomExtension, // <------------- using custom extension
    createExtension("color"),
    createExtension("youtube"),
  ],
})

Suggestions

We refer to suggestions as the list of items that appear when you type a special character such as @ or / in the editor. These suggestions can be used to mention users (or anything else) or to display shortcuts to the editor elements.

To use suggestions, we have two types of extensions that allow you to add suggestions:

  • mention
  • slash-command.

Both extensions can be used normally with createExtension. However, a new utility is added to facilitate their use: createSuggestion. This utility allows us to create suggestions in a simpler way without having to think about the development of the list of items and its configuration to work with the editor. For this we must pass as first parameter the name of the base extension.

  • If we create suggestions for mentions, by default it will offer us 5 generic names as initial sample.
  • If we create suggestions for commands, it will default to 10 quick access commands: p, all-list, h1, h2, h3, bullet-list, numbered-list, quote, code, and table.

To change them, we can pass an array of values inside the second function parameter.

The second parameter of the function is optional and allows us, in addition to the default values offered by the suggestion key, to add custom values.

  • If the array is for an extension of type mention, then the values must be an array of strings.
  • If the array is for a slash-command type extension, then the values must be an array of objects with the following structure:
Array<{
  title: string
  description: string
  searchTerms: string[]
  icon: JSX.Element
  command?: MentionOptions["suggestion"]["command"]
}>
💡

The use of createSuggestion is optional. The purpose of this utility is to be able to summarize the creation of suggestions in a few lines of code.

Example of use:

...
  createExtension("mention", {
    suggestion: createSuggestion("mention")
  }),
...

If we would like to add custom values:

...
  createExtension("mention", {
    suggestion: createSuggestion("mention", {
      values: ["value1", "value2", "value3"]
    })
  }),
...

In addition, if we would like to customize the configuration, we can use the same properties that are used inside the default suggestion object inside the options object:

...
  createExtension("mention", {
    suggestion: createSuggestion("mention", {
      options: {
        items: ...,
        ...
      }
    })
  }),
...

This is equivalent to doing it in the following way, but with the disadvantage that we will have to develop the whole operation from scratch:

...
  createExtension("mention", {
    suggestion:  {
      items: ...,
      ...
    }
  }),
...

API Reference

LyricalProvider

PropTypeDefault
editor*
Editor

LyricalContent

This component is used to render the content of the editor. It is a wrapper around EditorContent component of tiptap. It accepts all the props that a HTMLDivElement accepts.

💡
It must be used inside LyricalProvider component.

useLyrical

This is a wrapper around useEditor hook of tiptap. It accepts all the props that useEditor accepts. It extends the extensions array with some of our own extensions and also provides some default options to the editor.

View all the available extensions here on Tiptap Docs (opens in a new tab).

LyricalSelectionMenu

This component is used to render the selection menu of the editor. It is a wrapper around BubbleMenu component of tiptap.

See the full list of props here on Tiptap Docs (opens in a new tab).

💡
It must be used inside LyricalProvider component.

Editor

This is the editor instance that is returned by useLyrical hook. It is a wrapper around Editor instance of tiptap.

Here is the full documentation of Editor instance on Tiptap Docs (opens in a new tab).