Skip to main content

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.
name: string
name
string
required
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.
priority?: number
priority
number
default:"100"
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
      },
    }
  },
})

addInputRules()

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

configure()

Create a configured version of the extension.
extension.configure(options?: Partial<Options>): Extension<Options, Storage>
options
Partial<Options>
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())

Build docs developers (and LLMs) love