Commands are functions that manipulate the editor state. They’re the primary way you interact with Tiptap to change content, format text, and control the editor.
Using Commands
All commands are available through editor.commands:
// Set content
editor . commands . setContent ( '<p>Hello World!</p>' )
// Toggle formatting
editor . commands . toggleBold ()
editor . commands . toggleItalic ()
// Insert content
editor . commands . insertContent ( '<p>New paragraph</p>' )
// Set node type
editor . commands . setParagraph ()
editor . commands . setHeading ({ level: 1 })
Source: /home/daytona/workspace/source/packages/core/src/Editor.ts:232
Command Types
Tiptap provides three ways to execute commands:
Single Commands Execute one command immediately. editor . commands . toggleBold ()
Chained Commands Execute multiple commands in sequence. editor . chain ()
. toggleBold ()
. toggleItalic ()
. run ()
Can Commands Check if a command can be executed. editor . can (). toggleBold ()
Single Commands
Single commands execute immediately and return a boolean indicating success:
const success = editor . commands . toggleBold ()
if ( success ) {
console . log ( 'Bold was toggled' )
} else {
console . log ( 'Bold could not be toggled' )
}
Source: /home/daytona/workspace/source/packages/core/src/CommandManager.ts:28
Chained Commands
Chained commands batch multiple operations into a single transaction:
editor
. chain ()
. focus ()
. toggleBold ()
. toggleItalic ()
. run ()
You must call .run() at the end of a chain to execute the commands!
Chains stop executing if a command fails:
// If toggleBold fails, toggleItalic won't run
editor
. chain ()
. toggleBold () // fails
. toggleItalic () // won't run
. run ()
The chain returns true if all commands succeeded, false otherwise:
const success = editor
. chain ()
. toggleBold ()
. toggleItalic ()
. run ()
console . log ( success ) // true or false
Source: /home/daytona/workspace/source/packages/core/src/CommandManager.ts:59
Can Commands
Use editor.can() to check if a command can be executed without actually executing it:
if ( editor . can (). toggleBold ()) {
console . log ( 'Bold can be toggled' )
}
// Disable a button if the command can't run
const isBoldDisabled = ! editor . can (). toggleBold ()
You can also chain can() commands:
if ( editor . can (). chain (). toggleBold (). toggleItalic (). run ()) {
console . log ( 'Both bold and italic can be toggled' )
}
Source: /home/daytona/workspace/source/packages/core/src/CommandManager.ts:95
Command Categories
Content Commands
setContent
(content: string | JSONContent, options?) => boolean
Replace the entire document. // Set HTML
editor . commands . setContent ( '<p>Hello World!</p>' )
// Set JSON
editor . commands . setContent ({
type: 'doc' ,
content: [{ type: 'paragraph' , content: [{ type: 'text' , text: 'Hello' }] }]
})
// Don't emit update event
editor . commands . setContent ( '<p>Hello</p>' , { emitUpdate: false })
Source: /home/daytona/workspace/source/packages/core/src/commands/setContent.ts:35
insertContent
(content: string | JSONContent, options?) => boolean
Insert content at the current cursor position. editor . commands . insertContent ( '<p>New paragraph</p>' )
insertContentAt
(position: number | Range, content: string | JSONContent) => boolean
Insert content at a specific position. // Insert at position 10
editor . commands . insertContentAt ( 10 , '<p>Hello</p>' )
// Insert in a range
editor . commands . insertContentAt ({ from: 0 , to: 10 }, '<p>Hello</p>' )
clearContent
(emitUpdate?: boolean) => boolean
Clear the entire document. editor . commands . clearContent ()
Selection Commands
focus
(position?: 'start' | 'end' | number | boolean) => boolean
Focus the editor. // Focus at current position
editor . commands . focus ()
// Focus at start
editor . commands . focus ( 'start' )
// Focus at end
editor . commands . focus ( 'end' )
// Focus at position 10
editor . commands . focus ( 10 )
Remove focus from the editor.
setTextSelection
(position: number | Range) => boolean
Set the text selection. // Select position 10
editor . commands . setTextSelection ( 10 )
// Select from 0 to 10
editor . commands . setTextSelection ({ from: 0 , to: 10 })
Select the entire document. editor . commands . selectAll ()
Node Commands
setNode
(typeOrName: string | NodeType, attributes?) => boolean
Replace the current node with a different type. // Set to paragraph
editor . commands . setNode ( 'paragraph' )
// Set to heading with level
editor . commands . setNode ( 'heading' , { level: 1 })
toggleNode
(typeOrName: string | NodeType, toggleTypeOrName: string | NodeType, attributes?) => boolean
Toggle between two node types. // Toggle between heading and paragraph
editor . commands . toggleNode ( 'heading' , 'paragraph' , { level: 1 })
deleteNode
(typeOrName: string | NodeType) => boolean
Delete a specific node. editor . commands . deleteNode ( 'image' )
updateAttributes
(typeOrName: string | NodeType | MarkType, attributes: Record<string, any>) => boolean
Update attributes of the current node or mark. // Update heading level
editor . commands . updateAttributes ( 'heading' , { level: 2 })
// Update link href
editor . commands . updateAttributes ( 'link' , { href: 'https://example.com' })
Mark Commands
setMark
(typeOrName: string | MarkType, attributes?) => boolean
Apply a mark to the current selection. editor . commands . setMark ( 'bold' )
editor . commands . setMark ( 'link' , { href: 'https://example.com' })
toggleMark
(typeOrName: string | MarkType, attributes?, options?) => boolean
Toggle a mark on the current selection. // Toggle bold
editor . commands . toggleMark ( 'bold' )
// Toggle link with attributes
editor . commands . toggleMark ( 'link' , { href: 'https://example.com' })
// Extend empty mark range
editor . commands . toggleMark ( 'bold' , {}, { extendEmptyMarkRange: true })
Source: /home/daytona/workspace/source/packages/core/src/commands/toggleMark.ts:39
unsetMark
(typeOrName: string | MarkType, options?) => boolean
Remove a mark from the current selection. editor . commands . unsetMark ( 'bold' )
editor . commands . unsetMark ( 'link' )
Remove all marks from the current selection. editor . commands . unsetAllMarks ()
List Commands
toggleList
(listTypeOrName: string | NodeType, itemTypeOrName: string | NodeType) => boolean
Toggle a list. editor . commands . toggleList ( 'bulletList' , 'listItem' )
editor . commands . toggleList ( 'orderedList' , 'listItem' )
wrapInList
(typeOrName: string | NodeType, attributes?) => boolean
Wrap the current selection in a list. editor . commands . wrapInList ( 'bulletList' )
liftListItem
(typeOrName: string | NodeType) => boolean
Lift a list item out of its parent list. editor . commands . liftListItem ( 'listItem' )
sinkListItem
(typeOrName: string | NodeType) => boolean
Sink a list item into the previous list item. editor . commands . sinkListItem ( 'listItem' )
Creating Custom Commands
You can create custom commands in your extensions:
import { Extension } from '@tiptap/core'
declare module '@tiptap/core' {
interface Commands < ReturnType > {
myExtension : {
insertGreeting : () => ReturnType
setColor : ( color : string ) => ReturnType
}
}
}
export const MyExtension = Extension . create ({
name: 'myExtension' ,
addCommands () {
return {
insertGreeting : () => ({ commands }) => {
return commands . insertContent ( '<p>Hello!</p>' )
},
setColor : ( color : string ) => ({ commands , tr , state }) => {
// Access to transaction and state
return commands . updateAttributes ( 'textStyle' , { color })
},
}
},
})
Command Props
Commands receive props that give you access to the editor state:
addCommands () {
return {
myCommand : ( arg1 , arg2 ) => ({ commands , editor , state , tr , dispatch , chain , can }) => {
// commands: Access to all other commands
// editor: The editor instance
// state: Current editor state
// tr: Current transaction
// dispatch: Function to dispatch the transaction
// chain: Create a command chain
// can: Check if commands can run
return true // Return true if successful
},
}
}
Source: /home/daytona/workspace/source/packages/core/src/CommandManager.ts:112
Real Example: setParagraph
Here’s the actual implementation of the setParagraph command from the Paragraph extension:
addCommands () {
return {
setParagraph : () => ({ commands }) => {
return commands . setNode ( this . name )
},
}
}
Source: /home/daytona/workspace/source/packages/extension-paragraph/src/paragraph.ts:109
Real Example: toggleBold
Here’s the implementation from the Bold extension:
addCommands () {
return {
setBold : () => ({ commands }) => {
return commands . setMark ( this . name )
},
toggleBold : () => ({ commands }) => {
return commands . toggleMark ( this . name )
},
unsetBold : () => ({ commands }) => {
return commands . unsetMark ( this . name )
},
}
}
Source: /home/daytona/workspace/source/packages/extension-bold/src/bold.tsx:106
Advanced Command Usage
Conditional Execution
editor
. chain ()
. focus ()
. command (({ tr , state }) => {
// Custom command logic
if ( state . selection . empty ) {
return false
}
// Do something with the transaction
tr . insertText ( 'Hello' )
return true
})
. run ()
Accessing State in Commands
const customCommand = () => ({ tr , state , dispatch }) => {
const { selection } = state
const { from , to } = selection
// Get selected text
const text = state . doc . textBetween ( from , to )
console . log ( 'Selected text:' , text )
if ( dispatch ) {
tr . insertText ( text . toUpperCase (), from , to )
}
return true
}
editor . commands . command ( customCommand )
Checking Command Success
const success = editor
. chain ()
. focus ()
. toggleBold ()
. toggleItalic ()
. run ()
if ( ! success ) {
console . error ( 'Commands failed to execute' )
}
Common Command Patterns
const toggleBold = () => {
if ( editor . can (). toggleBold ()) {
editor . chain (). focus (). toggleBold (). run ()
}
}
Set Heading Level
const setHeadingLevel = ( level : number ) => {
editor
. chain ()
. focus ()
. toggleHeading ({ level })
. run ()
}
Insert Link
const insertLink = ( href : string ) => {
editor
. chain ()
. focus ()
. extendMarkRange ( 'link' )
. setLink ({ href })
. run ()
}
Remove Link
const removeLink = () => {
editor
. chain ()
. focus ()
. extendMarkRange ( 'link' )
. unsetLink ()
. run ()
}
Toggle List
const toggleBulletList = () => {
editor
. chain ()
. focus ()
. toggleBulletList ()
. run ()
}
TypeScript Types
import type {
SingleCommands ,
ChainedCommands ,
CanCommands ,
CommandProps ,
} from '@tiptap/core'
// Command function type
type CommandFunction = ( props : CommandProps ) => boolean
// Declare custom commands
declare module '@tiptap/core' {
interface Commands < ReturnType > {
myExtension : {
myCommand : ( arg : string ) => ReturnType
}
}
}
Best Practices
Always Use Chains for Multiple Commands Batch multiple commands into a single transaction for better performance and UX. // Bad: Multiple transactions
editor . commands . toggleBold ()
editor . commands . toggleItalic ()
// Good: Single transaction
editor . chain (). toggleBold (). toggleItalic (). run ()
Check Before Executing Use can() to check if a command can run before executing it. if ( editor . can (). toggleBold ()) {
editor . commands . toggleBold ()
}
Focus Before Editing Always focus the editor before running content commands. editor . chain (). focus (). toggleBold (). run ()
Return True on Success Custom commands should return true if successful, false otherwise. myCommand : () => ({ commands }) => {
const success = commands . insertContent ( 'Hello' )
return success
}
Editor Learn about the Editor class
Extensions Learn how to create commands in extensions
Nodes & Marks Learn about content structure
Schema Learn about the ProseMirror schema