添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
宽容的凉茶  ·  Pseudo Label Approach ...·  4 月前    · 
欢快的消炎药  ·  Java ...·  6 月前    · 
豪爽的香烟  ·  OPPO手机怎么png转jpg ...·  6 月前    · 

Introduction

The editor provides a ton of commands to programmatically add or change content or alter the selection. If you want to build your own editor you definitely want to learn more about them.

Execute a command

All available commands are accessible through an editor instance. Let’s say you want to make text bold when a user clicks on a button. That’s how that would look like:

editor.commands.setBold()

While that’s perfectly fine and does make the selected bold, you’d likely want to chain multiple commands in one run. Let’s have a look at how that works.

Chain commands

Most commands can be combined to one call. That’s shorter than separate function calls in most cases. Here is an example to make the selected text bold:

editor
  .chain()
  .focus()
  .toggleBold()
  .run()

The .chain() is required to start a new chain and the .run() is needed to actually execute all the commands in between.

In the example above two different commands are executed at once. When a user clicks on a button outside of the content, the editor isn’t in focus anymore. That’s why you probably want to add a .focus() call to most of your commands. It brings back the focus to the editor, so the user can continue to type.

All chained commands are kind of queued up. They are combined to one single transaction. That means, the content is only updated once, also the update event is only triggered once.

Important

By default Prosemirror does not support chaining which means that you need to update the positions between chained commands via Transaction mapping.

For example you want to chain a delete and insert command in one chain, you need to keep track of the position inside your chain commands. Here is an example:

// here we add two custom commands to the editor to demonstrate transaction mapping between two transaction steps
addCommands() {
  return {
    delete: () => ({ tr }) => {
      const { $from, $to } = tr.selection
      // here we use tr.mapping.map to map the position between transaction steps
      const from = tr.mapping.map($from.pos)
      const to = tr.mapping.map($to.pos)
      tr.delete(from, to)
      return true
    insert: (content: string) => ({ tr }) => {
      const { $from } = tr.selection
      // here we use tr.mapping.map to map the position between transaction steps
      const pos = tr.mapping.map($from.pos)
      tr.insertText(content, pos)
      return true

Now you can do the following without insert inserting the content into the wrong position:

editor.chain().delete().insert('foo').run()

Chaining inside custom commands

When chaining a command, the transaction is held back. If you want to chain commands inside your custom commands, you’ll need to use said transaction and add to it. Here is how you would do that:

addCommands() {
  return {
    customCommand: attributes => ({ chain }) => {
      // Doesn’t work:
      // return editor.chain() …
      // Does work:
      return chain()
        .insertContent('foo!')
        .insertContent('bar!')
        .run()

Inline commands

In some cases, it’s helpful to put some more logic in a command. That’s why you can execute commands in commands. I know, that sounds crazy, but let’s look at an example:

editor
  .chain()
  .focus()
  .command(({ tr }) => {
    // manipulate the transaction
    tr.insertText('hey, that’s cool!')
    return true
  .run()

Dry run for commands

Sometimes, you don’t want to actually run the commands, but only know if it would be possible to run commands, for example to show or hide buttons in a menu. That’s what we added .can() for. Everything coming after this method will be executed, without applying the changes to the document:

editor
  .can()
  .toggleBold()

And you can use it together with .chain(), too. Here is an example which checks if it’s possible to apply all the commands:

editor
  .can()
  .chain()
  .toggleBold()
  .toggleItalic()
  .run()

Both calls would return true if it’s possible to apply the commands, and false in case it’s not.

In order to make that work with your custom commands, don’t forget to return true or false.

For some of your own commands, you probably want to work with the raw transaction. To make them work with .can() you should check if the transaction should be dispatched. Here is how you can create a simple .insertText() command:

export default (value) => ({ tr, dispatch }) => {
  if (dispatch) {
    tr.insertText(value)
  return true

If you’re just wrapping another Tiptap command, you don’t need to check that, we’ll do it for you.

addCommands() {
  return {
    bold: () => ({ commands }) => {
      return commands.toggleMark('bold')

If you’re just wrapping a plain ProseMirror command, you’ll need to pass dispatch anyway. Then there’s also no need to check it:

import { exitCode } from '@tiptap/pm/commands'
export default () => ({ state, dispatch }) => {
  return exitCode(state, dispatch)

Try commands

If you want to run a list of commands, but want only the first successful command to be applied, you can do this with the .first() method. This method runs one command after the other and stops at the first which returns true.

For example, the backspace key tries to undo an input rule first. If that was successful, it stops there. If no input rule has been applied and thus can’t be reverted, it runs the next command and deletes the selection, if there is one. Here is the simplified example:

editor.first(({ commands }) => [
  () => commands.undoInputRule(),
  () => commands.deleteSelection(),

Inside of commands you can do the same thing:

export default () => ({ commands }) => {
  return commands.first([
    () => commands.undoInputRule(),
    () => commands.deleteSelection(),

List of commands

Have a look at all of the core commands listed below. They should give you a good first impression of what’s possible.

Content

Write your own commands

All extensions can add additional commands (and most do), check out the specific documentation for the provided nodes, marks, and extensions to learn more about those. And of course, you can add your custom extensions with custom commands aswell.

But how do you write those commands? There’s a little bit to learn about that.

Oops, this is work in progress

A well-written documentation needs attention to detail, a great understanding of the project and time to write. Though Tiptap is used by thousands of developers all around the world, it’s still a side project for us. Let’s change that and make open source our full-time job! With nearly 300 sponsors we are half way there already. Join them and become a sponsor! Enable us to put more time into open source and we’ll fill this page and keep it up to date for you. Become a sponsor on GitHub →