---
title: "Install the Tracked Changes extension"
description: "Install and configure the Tracked Changes extension for suggestion mode in your Tiptap editor."
canonical_url: "https://tiptap-docs.lin52025i.qzz.io/tracked-changes/getting-started/install"
---

# Install the Tracked Changes extension

Install and configure the Tracked Changes extension for suggestion mode in your Tiptap editor.

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

- **1. Get the add-on**

  Tracked Changes is a paid add-on that isn't included in any plan. Test it during your [free
  trial](https://cloud.tiptap.dev/v2?trial=true) or purchase it in [your
  dashboard](https://cloud.tiptap.dev/v2/billing).
- **2. Access the private registry**

  The Tracked Changes extension is published in Tiptap's private npm registry. Authenticate to
  Tiptap's private npm registry by following the [setup guide](https://tiptap-docs.lin52025i.qzz.io/guides/pro-extensions.md).
- **3. Install the extension**

  Install the extension from the private registry using npm or your preferred package manager.

## Install

Tracked Changes is a paid add-on that isn't included in any plan. Test it during your [free
trial](https://cloud.tiptap.dev/v2?trial=true) or purchase it in [your
dashboard](https://cloud.tiptap.dev/v2/billing).

Then configure your package manager by following the [private registry
guide](https://tiptap-docs.lin52025i.qzz.io/guides/pro-extensions.md).

Then, install the package:

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

## Basic setup

```js
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`

```js
TrackedChanges.configure({
  enabled: true,
})
```

### userId

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

Default: `'anonymous'`

```js
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`

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

### nodeTypeChangeTypes

Schema names of textblocks eligible for node type suggestions. Include both the source and target types. Converting a paragraph to a heading then creates one `attributeChange` suggestion while keeping one copy of the text. Conversions outside the configured pair keep the existing replacement behavior.

Default: `[]`

```js
TrackedChanges.configure({
  nodeTypeChangeTypes: ['paragraph', 'heading'],
})
```

The opt-in `nodeTypeChangeTypes` setting is available for local editors. When using Tiptap Collaboration (Y.js document), leave it at its default `[]` to keep tracking type conversions as replacements. Configuring a nonempty list when using Tiptap Collaboration (Y.js document) throws during editor initialization. See [node type changes](https://tiptap-docs.lin52025i.qzz.io/tracked-changes/usage/advanced-usage.md#node-type-changes) for supported conversions and review behavior.

### onSuggestionCreate

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

Default: `undefined`

```js
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`

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

### onSuggestionReject

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

Default: `undefined`

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

### blockSplitMarker

The marker shown where a [block split](https://tiptap-docs.lin52025i.qzz.io/tracked-changes/usage/advanced-usage.md#block-splits) was suggested. It is rendered at the end of the block before the split.

Default: `'¶'`

Pass a string to change the character:

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

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

```js
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:

```js
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](https://tiptap-docs.lin52025i.qzz.io/tracked-changes/api-reference/styling.md#block-split-marker) for the default CSS.
