> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/ueberdosis/tiptap/llms.txt
> Use this file to discover all available pages before exploring further.

# Extensions

> Extensions are the building blocks of Tiptap. Learn how to use and create extensions to add functionality to your editor.

Extensions are the modular building blocks of Tiptap. Every feature in Tiptap is packaged as an extension, whether it's a node (like paragraphs or headings), a mark (like bold or italic), or functionality (like history or placeholder).

## What are Extensions?

Tiptap has three types of extensions:

<CardGroup cols={3}>
  <Card title="Extensions" icon="puzzle-piece">
    Generic extensions that add functionality without defining content structure (e.g., `StarterKit`, `Placeholder`, `CharacterCount`)
  </Card>

  <Card title="Nodes" icon="cube">
    Block or inline content types that make up your document (e.g., `Paragraph`, `Heading`, `Image`)
  </Card>

  <Card title="Marks" icon="highlighter">
    Formatting that can be applied to text (e.g., `Bold`, `Italic`, `Link`)
  </Card>
</CardGroup>

All three types share the same underlying `Extendable` base class and can be used interchangeably in the `extensions` array.

## Using Extensions

Extensions are passed to the editor via the `extensions` option:

```typescript theme={null}
import { Editor } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { Bold } from '@tiptap/extension-bold'
import { Italic } from '@tiptap/extension-italic'

const editor = new Editor({
  extensions: [
    StarterKit,
    Bold,
    Italic,
  ],
})
```

### Configuring Extensions

Most extensions accept options that can be configured using the `configure()` method:

```typescript theme={null}
import { Editor } from '@tiptap/core'
import { Heading } from '@tiptap/extension-heading'
import { Link } from '@tiptap/extension-link'

const editor = new Editor({
  extensions: [
    Heading.configure({
      levels: [1, 2, 3], // Only allow h1, h2, h3
      HTMLAttributes: {
        class: 'my-heading',
      },
    }),
    Link.configure({
      openOnClick: false,
      HTMLAttributes: {
        class: 'my-link',
        rel: 'noopener noreferrer',
      },
    }),
  ],
})
```

## Extension Structure

Every extension is created using the `Extension.create()`, `Node.create()`, or `Mark.create()` static methods:

```typescript theme={null}
import { Extension } from '@tiptap/core'

export const MyExtension = Extension.create({
  name: 'myExtension',
  
  addOptions() {
    return {
      // Default options
      myOption: 'default value',
    }
  },
  
  addCommands() {
    return {
      myCommand: () => ({ commands }) => {
        // Command implementation
        return true
      },
    }
  },
  
  addKeyboardShortcuts() {
    return {
      'Mod-k': () => this.editor.commands.myCommand(),
    }
  },
  
  // More hooks...
})
```

Source: `/home/daytona/workspace/source/packages/core/src/Extension.ts:23`

## Extension API

### Configuration

<ResponseField name="name" type="string" required>
  The unique name of the extension. This is used to identify the extension.

  ```typescript theme={null}
  name: 'myExtension'
  ```
</ResponseField>

<ResponseField name="priority" type="number" default="100">
  The priority determines the order in which extensions are loaded. Higher priority extensions are loaded first.

  ```typescript theme={null}
  priority: 1000
  ```
</ResponseField>

<ResponseField name="addOptions" type="() => Options">
  Define default options for your extension.

  ```typescript theme={null}
  addOptions() {
    return {
      HTMLAttributes: {},
      openOnClick: true,
    }
  }
  ```
</ResponseField>

<ResponseField name="addStorage" type="() => Storage">
  Define storage that persists across the lifetime of the editor.

  ```typescript theme={null}
  addStorage() {
    return {
      count: 0,
      users: [],
    }
  }
  ```

  Access storage via `editor.storage.extensionName`:

  ```typescript theme={null}
  editor.storage.myExtension.count
  ```
</ResponseField>

### Commands

<ResponseField name="addCommands" type="() => Commands">
  Add commands that can be called via `editor.commands`.

  ```typescript theme={null}
  addCommands() {
    return {
      setAwesome: () => ({ commands }) => {
        return commands.insertContent('<p>Awesome!</p>')
      },
    }
  }
  ```

  Usage:

  ```typescript theme={null}
  editor.commands.setAwesome()
  ```
</ResponseField>

### Keyboard Shortcuts

<ResponseField name="addKeyboardShortcuts" type="() => Record<string, () => boolean>">
  Add keyboard shortcuts for your extension.

  ```typescript theme={null}
  addKeyboardShortcuts() {
    return {
      'Mod-b': () => this.editor.commands.toggleBold(),
      'Mod-Shift-x': () => this.editor.commands.myCommand(),
    }
  }
  ```

  <Info>
    `Mod` is `Cmd` on Mac and `Ctrl` on Windows/Linux.
  </Info>
</ResponseField>

### Input Rules

<ResponseField name="addInputRules" type="() => InputRule[]">
  Add input rules for markdown-style shortcuts.

  ```typescript theme={null}
  addInputRules() {
    return [
      markInputRule({
        find: /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*)$/,
        type: this.type,
      }),
    ]
  }
  ```

  This would convert `**text**` to bold text as you type.
</ResponseField>

### Paste Rules

<ResponseField name="addPasteRules" type="() => PasteRule[]">
  Add paste rules for handling pasted content.

  ```typescript theme={null}
  addPasteRules() {
    return [
      markPasteRule({
        find: /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*)/g,
        type: this.type,
      }),
    ]
  }
  ```
</ResponseField>

### ProseMirror Plugins

<ResponseField name="addProseMirrorPlugins" type="() => Plugin[]">
  Add ProseMirror plugins to extend functionality.

  ```typescript theme={null}
  addProseMirrorPlugins() {
    return [
      new Plugin({
        key: new PluginKey('myPlugin'),
        // Plugin configuration
      }),
    ]
  }
  ```
</ResponseField>

### Global Attributes

<ResponseField name="addGlobalAttributes" type="() => GlobalAttribute[]">
  Add attributes to multiple node or mark types.

  ```typescript theme={null}
  addGlobalAttributes() {
    return [
      {
        types: ['heading', 'paragraph'],
        attributes: {
          textAlign: {
            default: 'left',
            renderHTML: attributes => ({
              style: `text-align: ${attributes.textAlign}`,
            }),
            parseHTML: element => element.style.textAlign || 'left',
          },
        },
      },
    ]
  }
  ```
</ResponseField>

### Lifecycle Hooks

<ResponseField name="onCreate" type="() => void">
  Called when the editor is created.

  ```typescript theme={null}
  onCreate() {
    console.log('Extension initialized')
  }
  ```
</ResponseField>

<ResponseField name="onUpdate" type="() => void">
  Called when the editor content changes.

  ```typescript theme={null}
  onUpdate() {
    this.storage.count++
  }
  ```
</ResponseField>

<ResponseField name="onSelectionUpdate" type="() => void">
  Called when the selection changes.
</ResponseField>

<ResponseField name="onTransaction" type="({ transaction }) => void">
  Called for every transaction.
</ResponseField>

<ResponseField name="onFocus" type="({ event }) => void">
  Called when the editor receives focus.
</ResponseField>

<ResponseField name="onBlur" type="({ event }) => void">
  Called when the editor loses focus.
</ResponseField>

<ResponseField name="onDestroy" type="() => void">
  Called when the editor is destroyed. Use this to clean up resources.

  ```typescript theme={null}
  onDestroy() {
    // Clean up event listeners, timers, etc.
  }
  ```
</ResponseField>

## Creating an Extension

Here's a complete example of a custom extension:

```typescript theme={null}
import { Extension } from '@tiptap/core'
import { Plugin, PluginKey } from '@tiptap/pm/state'

export interface CharacterCountOptions {
  limit: number | null
}

export const CharacterCount = Extension.create<CharacterCountOptions>({
  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(Boolean).length
      },
    }
  },
  
  onCreate() {
    console.log(`Character limit: ${this.options.limit}`)
  },
  
  onUpdate() {
    const count = this.storage.characters()
    
    if (this.options.limit && count > this.options.limit) {
      console.warn('Character limit exceeded!')
    }
  },
  
  addProseMirrorPlugins() {
    return [
      new Plugin({
        key: new PluginKey('characterCount'),
        // Plugin logic
      }),
    ]
  },
})
```

### Usage

```typescript theme={null}
const editor = new Editor({
  extensions: [
    CharacterCount.configure({
      limit: 1000,
    }),
  ],
})

// Access storage
const count = editor.storage.characterCount.characters()
const words = editor.storage.characterCount.words()
```

## Extending Extensions

You can extend existing extensions to modify or add functionality:

```typescript theme={null}
import { Paragraph } from '@tiptap/extension-paragraph'

const CustomParagraph = Paragraph.extend({
  addAttributes() {
    return {
      ...this.parent?.(),
      textAlign: {
        default: 'left',
        renderHTML: attributes => ({
          style: `text-align: ${attributes.textAlign}`,
        }),
        parseHTML: element => element.style.textAlign || 'left',
      },
    }
  },
})
```

The `this.parent?.()` call merges the parent extension's attributes with your new ones.

## Extension Packages

Tiptap provides many official extensions:

### Starter Kit

```typescript theme={null}
import StarterKit from '@tiptap/starter-kit'

const editor = new Editor({
  extensions: [
    StarterKit,
  ],
})
```

The StarterKit includes:

* Document
* Paragraph
* Text
* Bold
* Italic
* Strike
* Code
* Heading
* Blockquote
* BulletList
* OrderedList
* ListItem
* CodeBlock
* HardBreak
* HorizontalRule
* History
* Dropcursor
* Gapcursor

### Individual Extensions

```typescript theme={null}
import { Bold } from '@tiptap/extension-bold'
import { Italic } from '@tiptap/extension-italic'
import { Underline } from '@tiptap/extension-underline'
import { Link } from '@tiptap/extension-link'
import { Image } from '@tiptap/extension-image'
import { Table } from '@tiptap/extension-table'
import { TableRow } from '@tiptap/extension-table-row'
import { TableCell } from '@tiptap/extension-table-cell'
import { Placeholder } from '@tiptap/extension-placeholder'
import { CharacterCount } from '@tiptap/extension-character-count'
```

## Real-World Example: Bold Extension

Here's the actual implementation of the Bold extension from the Tiptap source:

```typescript theme={null}
import { Mark, markInputRule, markPasteRule, mergeAttributes } from '@tiptap/core'

export interface BoldOptions {
  HTMLAttributes: Record<string, any>
}

declare module '@tiptap/core' {
  interface Commands<ReturnType> {
    bold: {
      setBold: () => ReturnType
      toggleBold: () => ReturnType
      unsetBold: () => ReturnType
    }
  }
}

const starInputRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))$/
const starPasteRegex = /(?:^|\s)(\*\*(?!\s+\*\*)((?:[^*]+))\*\*(?!\s+\*\*))/g

export const Bold = Mark.create<BoldOptions>({
  name: 'bold',
  
  addOptions() {
    return {
      HTMLAttributes: {},
    }
  },
  
  parseHTML() {
    return [
      { tag: 'strong' },
      { tag: 'b', getAttrs: node => (node as HTMLElement).style.fontWeight !== 'normal' && null },
      { style: 'font-weight', getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value as string) && null },
    ]
  },
  
  renderHTML({ HTMLAttributes }) {
    return ['strong', mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]
  },
  
  addCommands() {
    return {
      setBold: () => ({ commands }) => commands.setMark(this.name),
      toggleBold: () => ({ commands }) => commands.toggleMark(this.name),
      unsetBold: () => ({ commands }) => commands.unsetMark(this.name),
    }
  },
  
  addKeyboardShortcuts() {
    return {
      'Mod-b': () => this.editor.commands.toggleBold(),
      'Mod-B': () => this.editor.commands.toggleBold(),
    }
  },
  
  addInputRules() {
    return [
      markInputRule({ find: starInputRegex, type: this.type }),
    ]
  },
  
  addPasteRules() {
    return [
      markPasteRule({ find: starPasteRegex, type: this.type }),
    ]
  },
})
```

Source: `/home/daytona/workspace/source/packages/extension-bold/src/bold.tsx:56`

## TypeScript Support

```typescript theme={null}
import { Extension } from '@tiptap/core'
import type { Editor } from '@tiptap/core'

interface MyOptions {
  color: string
  size: number
}

interface MyStorage {
  count: number
}

export const MyExtension = Extension.create<MyOptions, MyStorage>({
  name: 'myExtension',
  
  addOptions() {
    return {
      color: 'blue',
      size: 12,
    }
  },
  
  addStorage() {
    return {
      count: 0,
    }
  },
})
```

## Best Practices

<Card title="Unique Names" icon="fingerprint">
  Always use unique extension names to avoid conflicts.

  ```typescript theme={null}
  name: 'myCompany_myExtension'
  ```
</Card>

<Card title="Clean Up Resources" icon="broom">
  Use `onDestroy` to clean up event listeners, timers, and other resources.

  ```typescript theme={null}
  onDestroy() {
    this.observer?.disconnect()
    clearInterval(this.timer)
  }
  ```
</Card>

<Card title="Use TypeScript" icon="code">
  Define types for your options and storage for better developer experience.
</Card>

<Card title="Test Thoroughly" icon="flask">
  Extensions can interact in unexpected ways. Test your extension with various combinations of other extensions.
</Card>

## Related

<CardGroup cols={2}>
  <Card title="Nodes & Marks" icon="cube" href="/core-concepts/nodes-and-marks">
    Learn about creating node and mark extensions
  </Card>

  <Card title="Commands" icon="terminal" href="/core-concepts/commands">
    Learn about the command system
  </Card>

  <Card title="Schema" icon="diagram-project" href="/core-concepts/schema">
    Learn about how extensions generate the schema
  </Card>

  <Card title="Editor" icon="pen-to-square" href="/core-concepts/editor">
    Learn about the Editor class
  </Card>
</CardGroup>
