Creating an Extension
Use the static create method to define a new extension.
import { Extension } from '@tiptap/core'
const MyExtension = Extension.create<Options, Storage>({
name: 'myExtension',
// ... configuration
})
config
Partial<ExtensionConfig<Options, Storage>> | (() => Partial<ExtensionConfig<Options, Storage>>)
The extension configuration object or a function that returns a configuration object.
Configuration Options
name
The unique name for the extension.
A unique identifier for the extension. This must be unique across all extensions.
Example
const MyExtension = Extension.create({
name: 'myExtension',
})
priority
The priority of the extension. Higher values are called earlier.
Extensions with higher priority take precedence. Core extensions typically use 100.
Example
const HighPriorityExtension = Extension.create({
name: 'highPriority',
priority: 1000,
})
addOptions()
Define options for the extension.
addOptions?(this: { name: string; parent: ParentConfig['addOptions'] }): Options
Example
interface MyExtensionOptions {
color: string
size: number
}
const MyExtension = Extension.create<MyExtensionOptions>({
name: 'myExtension',
addOptions() {
return {
color: 'blue',
size: 16,
}
},
})
addStorage()
Define storage for the extension to persist data.
addStorage?(this: {
name: string
options: Options
parent: ParentConfig['addStorage']
}): Storage
Example
interface MyExtensionStorage {
count: number
}
const MyExtension = Extension.create<{}, MyExtensionStorage>({
name: 'myExtension',
addStorage() {
return {
count: 0,
}
},
addCommands() {
return {
incrementCount: () => ({ editor }) => {
editor.storage.myExtension.count++
return true
},
}
},
})
addGlobalAttributes()
Add attributes to specific node or mark types.
addGlobalAttributes?(this: {
name: string
options: Options
storage: Storage
extensions: (Node | Mark)[]
parent: ParentConfig['addGlobalAttributes']
}): GlobalAttributes
Example
const TextAlign = Extension.create({
name: 'textAlign',
addGlobalAttributes() {
return [
{
types: ['heading', 'paragraph'],
attributes: {
textAlign: {
default: 'left',
parseHTML: element => element.style.textAlign || 'left',
renderHTML: attributes => ({
style: `text-align: ${attributes.textAlign}`,
}),
},
},
},
]
},
})
addCommands()
Add commands to the editor.
addCommands?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['addCommands']
}): Partial<RawCommands>
Example
const MyExtension = Extension.create({
name: 'myExtension',
addCommands() {
return {
setColor: (color: string) => ({ commands }) => {
return commands.updateAttributes('textStyle', { color })
},
}
},
})
// Usage
editor.commands.setColor('red')
addKeyboardShortcuts()
Register keyboard shortcuts.
addKeyboardShortcuts?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['addKeyboardShortcuts']
}): { [key: string]: KeyboardShortcutCommand }
Example
const MyExtension = Extension.create({
name: 'myExtension',
addKeyboardShortcuts() {
return {
'Mod-Shift-x': () => this.editor.commands.clearContent(),
'Mod-k': () => {
console.log('Keyboard shortcut triggered')
return true
},
}
},
})
Add input rules that trigger on typing patterns.
addInputRules?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['addInputRules']
}): InputRule[]
Example
import { markInputRule } from '@tiptap/core'
const MyExtension = Extension.create({
name: 'myExtension',
addInputRules() {
return [
markInputRule({
find: /(?:^|\s)((?:==)((?:[^=]+))(?:==))$/,
type: this.editor.schema.marks.highlight,
}),
]
},
})
addPasteRules()
Add paste rules that trigger on pasting content.
addPasteRules?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['addPasteRules']
}): PasteRule[]
Example
import { markPasteRule } from '@tiptap/core'
const MyExtension = Extension.create({
name: 'myExtension',
addPasteRules() {
return [
markPasteRule({
find: /https?:\/\/[^\s]+/g,
type: this.editor.schema.marks.link,
getAttributes: match => ({ href: match[0] }),
}),
]
},
})
addProseMirrorPlugins()
Add ProseMirror plugins.
addProseMirrorPlugins?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['addProseMirrorPlugins']
}): Plugin[]
Example
import { Plugin, PluginKey } from '@tiptap/pm/state'
const MyExtension = Extension.create({
name: 'myExtension',
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey('myPlugin'),
props: {
handleClick(view, pos, event) {
console.log('Editor clicked at position:', pos)
return false
},
},
}),
]
},
})
addExtensions()
Add additional extensions (useful for extension kits).
addExtensions?(this: {
name: string
options: Options
storage: Storage
parent: ParentConfig['addExtensions']
}): Extensions
Example
import { BulletList, OrderedList, ListItem } from '@tiptap/extension-list'
const ListKit = Extension.create({
name: 'listKit',
addExtensions() {
return [BulletList, OrderedList, ListItem]
},
})
Lifecycle Hooks
onBeforeCreate()
Called before the editor is created.
onBeforeCreate?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onBeforeCreate']
}, event: { editor: Editor }): void
onCreate()
Called when the editor is ready.
onCreate?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onCreate']
}, event: { editor: Editor }): void
onUpdate()
Called when the content changes.
onUpdate?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onUpdate']
}, event: { editor: Editor; transaction: Transaction }): void
onSelectionUpdate()
Called when the selection changes.
onSelectionUpdate?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onSelectionUpdate']
}, event: { editor: Editor; transaction: Transaction }): void
onTransaction()
Called after each transaction.
onTransaction?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onTransaction']
}, event: { editor: Editor; transaction: Transaction }): void
onFocus()
Called when the editor gains focus.
onFocus?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onFocus']
}, event: { editor: Editor; event: FocusEvent; transaction: Transaction }): void
onBlur()
Called when the editor loses focus.
onBlur?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onBlur']
}, event: { editor: Editor; event: FocusEvent; transaction: Transaction }): void
onDestroy()
Called when the editor is destroyed.
onDestroy?(this: {
name: string
options: Options
storage: Storage
editor: Editor
type: null
parent: ParentConfig['onDestroy']
}): void
Methods
Create a configured version of the extension.
extension.configure(options?: Partial<Options>): Extension<Options, Storage>
Options to override the default options.
Example
const MyExtension = Extension.create<{ color: string }>({
name: 'myExtension',
addOptions() {
return { color: 'blue' }
},
})
// Use with custom options
const editor = new Editor({
extensions: [
MyExtension.configure({ color: 'red' }),
],
})
extend()
Extend the extension with additional configuration.
extension.extend<ExtendedOptions, ExtendedStorage, ExtendedConfig>(
extendedConfig?: Partial<ExtendedConfig> | (() => Partial<ExtendedConfig>)
): Extension<ExtendedOptions, ExtendedStorage>
extendedConfig
Partial<ExtendedConfig> | (() => Partial<ExtendedConfig>)
Additional configuration or a function that returns configuration.
Example
const BaseExtension = Extension.create({
name: 'base',
addCommands() {
return {
doSomething: () => () => true,
}
},
})
const ExtendedExtension = BaseExtension.extend({
name: 'extended',
addCommands() {
return {
...this.parent?.(),
doSomethingElse: () => () => true,
}
},
})
Complete Example
import { Extension } from '@tiptap/core'
import { Plugin, PluginKey } from '@tiptap/pm/state'
interface CharacterCountOptions {
limit: number | null
}
interface CharacterCountStorage {
characters: () => number
words: () => number
}
const CharacterCount = Extension.create<CharacterCountOptions, CharacterCountStorage>({
name: 'characterCount',
addOptions() {
return {
limit: null,
}
},
addStorage() {
return {
characters: () => {
return this.editor.state.doc.textContent.length
},
words: () => {
return this.editor.state.doc.textContent.split(/\s+/).filter(word => word).length
},
}
},
addProseMirrorPlugins() {
return [
new Plugin({
key: new PluginKey('characterCount'),
filterTransaction: (transaction, state) => {
const limit = this.options.limit
if (!limit || !transaction.docChanged) {
return true
}
const newLength = transaction.doc.textContent.length
return newLength <= limit
},
}),
]
},
onCreate() {
console.log('Character count extension initialized')
},
onUpdate() {
const count = this.storage.characters()
console.log(`Character count: ${count}`)
},
})
// Usage
const editor = new Editor({
extensions: [
CharacterCount.configure({
limit: 1000,
}),
],
})
console.log('Characters:', editor.storage.characterCount.characters())
console.log('Words:', editor.storage.characterCount.words())