Install the Tracked Changes extension

Paid add-on

Install and configure the Tracked Changes extension by following this guide.

Install

First, contact our team to get access to the paid Tracked Changes add-on.

After gaining access, configure your package manager by following the private registry guide.

Then, install the package:

npm install @tiptap-pro/extension-tracked-changes

Basic setup

import { Editor } from '@tiptap/core'
import { TrackedChanges } from '@tiptap-pro/extension-tracked-changes'

const editor = new Editor({
  extensions: [
    TrackedChanges.configure({
      enabled: true,
      userId: 'user-123',
      userMetadata: { name: 'John Doe' },
    }),
  ],
})

Settings

enabled

Enable or disable track changes mode. When enabled, all edits become suggestions instead of direct changes.

Default: false

TrackedChanges.configure({
  enabled: true,
})

userId

The ID of the current user making suggestions. This should come from your authentication system.

Default: 'anonymous'

TrackedChanges.configure({
  userId: 'user-123',
})

userMetadata

Arbitrary metadata about the current user, stored as a JSON-serializable object on each suggestion. Useful for storing display names, avatars, or other custom data alongside the suggestion without requiring a separate user store.

Default: null

TrackedChanges.configure({
  userId: 'user-123',
  userMetadata: {
    name: 'John Doe',
    avatar: 'https://example.com/avatar.jpg',
    role: 'editor',
  },
})

onSuggestionCreate

Callback fired when a new suggestion is created. Receives the suggestion object containing id, type, userId, createdAt, from, to, and text.

Default: undefined

TrackedChanges.configure({
  onSuggestionCreate: (suggestion) => {
    console.log('New suggestion created:', suggestion)
    // Update your UI, notify other users, etc.
  },
})

onSuggestionAccept

Callback fired when a suggestion is accepted. Receives the suggestion ID.

Default: undefined

TrackedChanges.configure({
  onSuggestionAccept: (id) => {
    console.log('Suggestion accepted:', id)
  },
})

onSuggestionReject

Callback fired when a suggestion is rejected. Receives the suggestion ID.

Default: undefined

TrackedChanges.configure({
  onSuggestionReject: (id) => {
    console.log('Suggestion rejected:', id)
  },
})

blockSplitMarker

The marker shown where a block split was suggested. It is rendered at the end of the block before the split.

Default: '¶'

Pass a string to change the character:

TrackedChanges.configure({
  blockSplitMarker: '↵',
})

Pass an element, or a function returning an element, for full control over the markup:

TrackedChanges.configure({
  // A function is called once per marker and must return a new element each time
  blockSplitMarker: () => {
    const marker = document.createElement('span')

    marker.className = 'my-split-marker'
    marker.textContent = '¶'

    return marker
  },
})

Pass null to render no marker at all:

TrackedChanges.configure({
  blockSplitMarker: null,
})

A single HTMLElement also works and is cloned for each marker. Whatever you return, the extension sets data-tracked-change-split="true", contenteditable="false", and disables selection and pointer events on it, so the marker never becomes editable content. See styling for the default CSS.