tiptap/docs/api/commands.md
2023-05-10 16:50:14 -06:00

13 KiB
Raw Blame History

tableOfContents
true

Commands

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. Lets say you want to make text bold when a user clicks on a button. Thats how that would look like:

editor.commands.setBold()

While thats perfectly fine and does make the selected bold, youd likely want to chain multiple commands in one run. Lets have a look at how that works.

Chain commands

Most commands can be combined to one call. Thats 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 isnt in focus anymore. Thats 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.

Chaining inside custom commands

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

addCommands() {
  return {
    customCommand: attributes => ({ chain }) => {
      // Doesnt work:
      // return editor.chain() …

      // Does work:
      return chain()
        .insertContent('foo!')
        .insertContent('bar!')
        .run()
    },
  }
}

Inline commands

In some cases, its helpful to put some more logic in a command. Thats why you can execute commands in commands. I know, that sounds crazy, but lets look at an example:

editor
  .chain()
  .focus()
  .command(({ tr }) => {
    // manipulate the transaction
    tr.insertText('hey, thats cool!')

    return true
  })
  .run()

Dry run for commands

Sometimes, you dont 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. Thats 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 its possible to apply all the commands:

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

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

In order to make that work with your custom commands, dont 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 youre just wrapping another Tiptap command, you dont need to check that, well do it for you.

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

If youre just wrapping a plain ProseMirror command, youll need to pass dispatch anyway. Then theres 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 cant 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 like that:

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 whats possible.

Content

Command Description Links
clearContent() Clear the whole document. More
insertContent() Insert a node or string of HTML at the current position. More
insertContentAt() Insert a node or string of HTML at a specific position. More
setContent() Replace the whole document with new content. More

Nodes & Marks

Command Description Links
clearNodes() Normalize nodes to a simple paragraph. More
createParagraphNear() Create a paragraph nearby. More
deleteNode() Delete a node. More
extendMarkRange() Extends the text selection to the current mark. More
exitCode() Exit from a code block. More
joinBackward() Join two nodes backward. More
joinForward() Join two nodes forward. More
lift() Removes an existing wrap. More
liftEmptyBlock() Lift block if empty. More
newlineInCode() Add a newline character in code. More
resetAttributes() Resets some node or mark attributes to the default value. More
setMark() Add a mark with new attributes. More
setNode() Replace a given range with a node. More
splitBlock() Forks a new node from an existing node. More
toggleMark() Toggle a mark on and off. More
toggleNode() Toggle a node with another node. More
toggleWrap() Wraps nodes in another node, or removes an existing wrap. More
undoInputRule() Undo an input rule. More
unsetAllMarks() Remove all marks in the current selection. More
unsetMark() Remove a mark in the current selection. More
updateAttributes() Update attributes of a node or mark. More

Lists

Command Description Links
liftListItem() Lift the list item into a wrapping list. More
sinkListItem() Sink the list item down into an inner list. More
splitListItem() Splits one list item into two list items. More
toggleList() Toggle between different list types. More
wrapInList() Wrap a node in a list. More

Selection

Command Description Links
blur() Removes focus from the editor. More
deleteRange() Delete a given range. More
deleteSelection() Delete the selection, if there is one. More
enter() Trigger enter. More
focus() Focus the editor at the given position. More
keyboardShortcut() Trigger a keyboard shortcut. More
scrollIntoView() Scroll the selection into view. More
selectAll() Select the whole document. More
selectNodeBackward() Select a node backward. More
selectNodeForward() Select a node forward. More
selectParentNode() Select the parent node. More
setNodeSelection() Creates a NodeSelection. More
setTextSelection() Creates a TextSelection. More

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? Theres a little bit to learn about that.

:::pro 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, its still a side project for us. Lets 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 well fill this page and keep it up to date for you.

Become a sponsor on GitHub → :::