Custom Extensions
Extensions are the building blocks of Tiptap. They allow you to add new functionality to the editor without modifying nodes or marks. Extensions are perfect for adding editor-wide features like commands, keyboard shortcuts, or plugins.
Understanding Extensions
The Extension class is the base class for all extensions in Tiptap. It provides a rich API for extending the editorโs functionality.
import { Extension } from '@tiptap/core'
export const MyExtension = Extension . create ({
name: 'myExtension' ,
addOptions () {
return {
// Default options
}
},
addCommands () {
return {
// Custom commands
}
},
addKeyboardShortcuts () {
return {
// Keyboard shortcuts
}
},
addProseMirrorPlugins () {
return [
// ProseMirror plugins
]
},
})
Creating a Simple Extension
Letโs create a simple extension that replaces emoticons with emojis as you type.
import { Extension , textInputRule } from '@tiptap/core'
export const SmilieReplacer = Extension . create ({
name: 'smilieReplacer' ,
addInputRules () {
return [
textInputRule ({ find: /:- \) $ / , replace: '๐ ' }),
textInputRule ({ find: /:D $ / , replace: '๐ ' }),
textInputRule ({ find: /;- \) $ / , replace: '๐ ' }),
textInputRule ({ find: /:- \( $ / , replace: '๐ ' }),
textInputRule ({ find: /<3 $ / , replace: 'โค๏ธ ' }),
textInputRule ({ find: / \/ shrug $ / , replace: 'ยฏ \\ _(ใ)_/ยฏ' }),
]
},
})
Source: demos/src/Examples/Savvy/React/SmilieReplacer.ts:1
Extension with ProseMirror Plugins
Hereโs a more advanced extension that uses ProseMirror plugins to highlight color codes in the editor.
ColorHighlighter Extension
import { Extension } from '@tiptap/core'
import { Plugin } from '@tiptap/pm/state'
import findColors from './findColors'
export const ColorHighlighter = Extension . create ({
name: 'colorHighlighter' ,
addProseMirrorPlugins () {
return [
new Plugin ({
state: {
init ( _ , { doc }) {
return findColors ( doc )
},
apply ( transaction , oldState ) {
return transaction . docChanged
? findColors ( transaction . doc )
: oldState
},
},
props: {
decorations ( state ) {
return this . getState ( state )
},
},
}),
]
},
})
Source: demos/src/Examples/Savvy/React/ColorHighlighter.ts:1
Extension Configuration
Adding Options
Extensions can accept configuration options that customize their behavior.
import { Extension } from '@tiptap/core'
interface MyExtensionOptions {
prefix : string
maxLength : number
}
export const MyExtension = Extension . create < MyExtensionOptions >({
name: 'myExtension' ,
addOptions () {
return {
prefix: 'default' ,
maxLength: 100 ,
}
},
addCommands () {
return {
insertPrefix : () => ({ commands }) => {
return commands . insertContent ( this . options . prefix )
},
}
},
})
Configuring Extensions
import { Editor } from '@tiptap/core'
import { MyExtension } from './my-extension'
const editor = new Editor ({
extensions: [
MyExtension . configure ({
prefix: 'custom' ,
maxLength: 200 ,
}),
],
})
Extension Storage
Extensions can maintain their own state using storage.
import { Extension } from '@tiptap/core'
interface MyStorage {
count : number
items : string []
}
export const MyExtension = Extension . create <{}, MyStorage >({
name: 'myExtension' ,
addStorage () {
return {
count: 0 ,
items: [],
}
},
onCreate () {
this . storage . count = 0
},
addCommands () {
return {
incrementCount : () => ({ editor }) => {
this . storage . count ++
return true
},
addItem : ( item : string ) => ({ editor }) => {
this . storage . items . push ( item )
return true
},
}
},
})
Lifecycle Hooks
Extensions can hook into various editor lifecycle events.
import { Extension } from '@tiptap/core'
export const MyExtension = Extension . create ({
name: 'myExtension' ,
onBeforeCreate () {
// Before the editor is created
console . log ( 'Before create' )
},
onCreate () {
// Editor is ready
console . log ( 'Editor created' )
},
onUpdate () {
// Content has changed
console . log ( 'Content updated' )
},
onSelectionUpdate () {
// Selection has changed
console . log ( 'Selection changed' )
},
onTransaction ({ transaction }) {
// On every transaction
console . log ( 'Transaction:' , transaction )
},
onFocus () {
// Editor gained focus
console . log ( 'Editor focused' )
},
onBlur () {
// Editor lost focus
console . log ( 'Editor blurred' )
},
onDestroy () {
// Editor is being destroyed
console . log ( 'Editor destroyed' )
},
})
Source: packages/core/src/Extendable.ts:341
Keyboard Shortcuts
Add custom keyboard shortcuts to your extension.
import { Extension } from '@tiptap/core'
export const MyExtension = Extension . create ({
name: 'myExtension' ,
addKeyboardShortcuts () {
return {
// Mod is Cmd on Mac, Ctrl on Windows/Linux
'Mod-k' : () => {
console . log ( 'Mod-k pressed' )
return true
},
// Multiple modifiers
'Mod-Shift-z' : () => {
this . editor . commands . redo ()
return true
},
// Handle Enter key
'Enter' : () => {
return this . editor . commands . first ([
() => this . editor . commands . newlineInCode (),
() => this . editor . commands . createParagraphNear (),
])
},
}
},
})
Source: packages/core/src/Extendable.ts:133
Global Attributes
Extensions can add attributes to other nodes or marks.
import { Extension } from '@tiptap/core'
export const TextAlign = Extension . create ({
name: 'textAlign' ,
addOptions () {
return {
types: [ 'heading' , 'paragraph' ],
alignments: [ 'left' , 'center' , 'right' , 'justify' ],
defaultAlignment: 'left' ,
}
},
addGlobalAttributes () {
return [
{
types: this . options . types ,
attributes: {
textAlign: {
default: this . options . defaultAlignment ,
parseHTML : element => element . style . textAlign || this . options . defaultAlignment ,
renderHTML : attributes => {
if ( attributes . textAlign === this . options . defaultAlignment ) {
return {}
}
return { style: `text-align: ${ attributes . textAlign } ` }
},
},
},
},
]
},
addCommands () {
return {
setTextAlign : ( alignment : string ) => ({ commands }) => {
if ( ! this . options . alignments . includes ( alignment )) {
return false
}
return this . options . types . every ( type =>
commands . updateAttributes ( type , { textAlign: alignment })
)
},
}
},
})
Source: packages/core/src/Extendable.ts:79
Priority
Control the order in which extensions are loaded and executed.
import { Extension } from '@tiptap/core'
export const MyExtension = Extension . create ({
name: 'myExtension' ,
priority: 1000 , // Higher priority loads earlier (default: 100)
// This extension will take precedence over lower-priority extensions
})
Source: packages/core/src/Extendable.ts:50
Extending Existing Extensions
You can extend existing extensions to customize their behavior.
import { Extension } from '@tiptap/core'
import { SmilieReplacer } from './smilie-replacer'
export const CustomSmilieReplacer = SmilieReplacer . extend ({
addInputRules () {
return [
// Keep parent rules
... this . parent ?.() || [],
// Add new rules
textInputRule ({ find: /:wave: $ / , replace: '๐ ' }),
textInputRule ({ find: /:fire: $ / , replace: '๐ฅ ' }),
]
},
})
Source: packages/core/src/Extension.ts:35
Extensions are ideal for adding editor-wide functionality. Use Nodes for block content and Marks for inline formatting.
Next Steps
Custom Nodes Learn how to create custom block content
Custom Marks Create custom inline formatting