---
title: "Utilities"
description: "Utility functions for querying suggestions and exporting base document JSON in the Tiptap Tracked Changes extension."
canonical_url: "https://tiptap-docs.lin52025i.qzz.io/tracked-changes/api-reference/utilities"
---

# Utilities

Utility functions for querying suggestions and exporting base document JSON in the Tiptap Tracked Changes extension.

The extension exports utility functions for querying suggestions and exporting base document JSON.

## findSuggestions()

Find all suggestions in the document, with optional filtering.

| Argument   | Type                     | Default        | Description                     |
| ---------- | ------------------------ | -------------- | ------------------------------- |
| `editor`   | `Editor`                 | —              | The Tiptap editor instance      |
| `markName` | `string`                 | `'suggestion'` | The name of the suggestion mark |
| `options`  | `FindSuggestionsOptions` | `{}`           | Optional filters (see below)    |

### Options

| Option   | Type             | Description                                                                                                             |
| -------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `type`   | `SuggestionType` | Filter by [suggestion type](https://tiptap-docs.lin52025i.qzz.io/tracked-changes/api-reference/types.md#suggestiontype) |
| `userId` | `string`         | Filter by user ID                                                                                                       |
| `id`     | `string`         | Filter by suggestion ID                                                                                                 |

### Returns

`Suggestion[]` — Array of suggestions found in the document.

```js
import { findSuggestions } from '@tiptap-pro/extension-tracked-changes'

// Find all suggestions
const allSuggestions = findSuggestions(editor)

// Find suggestions by type
const insertions = findSuggestions(editor, 'suggestion', { type: 'add' })
const deletions = findSuggestions(editor, 'suggestion', { type: 'delete' })
const replacements = findSuggestions(editor, 'suggestion', { type: 'replace' })
const splits = findSuggestions(editor, 'suggestion', { type: 'split' })

// Find suggestions by user
const userSuggestions = findSuggestions(editor, 'suggestion', { userId: 'user-123' })
```

## findSuggestionById()

Find a specific suggestion by ID.

| Argument   | Type     | Default        | Description                     |
| ---------- | -------- | -------------- | ------------------------------- |
| `editor`   | `Editor` | —              | The Tiptap editor instance      |
| `id`       | `string` | —              | The suggestion ID to find       |
| `markName` | `string` | `'suggestion'` | The name of the suggestion mark |

### Returns

`Suggestion | undefined` — The suggestion if found, `undefined` otherwise.

```js
import { findSuggestionById } from '@tiptap-pro/extension-tracked-changes'

const suggestion = findSuggestionById(editor, 'suggestion-123')

if (suggestion) {
  console.log(`Found suggestion: ${suggestion.text}`)
}
```

## findSuggestionRanges()

Find all document ranges covered by a suggestion. A single suggestion may span multiple text nodes when it crosses formatting boundaries.

| Argument   | Type     | Default        | Description                     |
| ---------- | -------- | -------------- | ------------------------------- |
| `editor`   | `Editor` | —              | The Tiptap editor instance      |
| `id`       | `string` | —              | The suggestion ID to find       |
| `markName` | `string` | `'suggestion'` | The name of the suggestion mark |

### Returns

`Array<{ from: number; to: number }>` — Array of position ranges covered by this suggestion. Adjacent ranges are automatically merged.

```js
import { findSuggestionRanges } from '@tiptap-pro/extension-tracked-changes'

const ranges = findSuggestionRanges(editor, 'suggestion-123')
// Returns: [{ from: 10, to: 25 }, { from: 30, to: 35 }]
```

## getSuggestionAtSelection()

Get the suggestion at the current selection position. Works with both mark-based suggestions (text) and attribute-based suggestions (atom nodes).

| Argument   | Type     | Default        | Description                     |
| ---------- | -------- | -------------- | ------------------------------- |
| `editor`   | `Editor` | —              | The Tiptap editor instance      |
| `markName` | `string` | `'suggestion'` | The name of the suggestion mark |

### Returns

`Suggestion | undefined` — The suggestion at the current cursor position, or `undefined` if none.

```js
import { getSuggestionAtSelection } from '@tiptap-pro/extension-tracked-changes'

const suggestion = getSuggestionAtSelection(editor)

if (suggestion) {
  // Show accept/reject buttons in your UI
  showSuggestionPopup(suggestion)
}
```

## getSuggestionAtPosition()

Get the suggestion at a specific document position.

| Argument   | Type     | Default        | Description                     |
| ---------- | -------- | -------------- | ------------------------------- |
| `editor`   | `Editor` | —              | The Tiptap editor instance      |
| `pos`      | `number` | —              | The document position to check  |
| `markName` | `string` | `'suggestion'` | The name of the suggestion mark |

### Returns

`Suggestion | null` — The suggestion at the position, or `null` if none found.

```js
import { getSuggestionAtPosition } from '@tiptap-pro/extension-tracked-changes'

const suggestion = getSuggestionAtPosition(editor, 42)
```

## findSuggestionsInRange()

Find all suggestions that overlap with a specific document range.

| Argument   | Type                                         | Default        | Description                     |
| ---------- | -------------------------------------------- | -------------- | ------------------------------- |
| `editor`   | `Editor`                                     | —              | The Tiptap editor instance      |
| `from`     | `number`                                     | —              | Start position of the range     |
| `to`       | `number`                                     | —              | End position of the range       |
| `filters`  | `{ userId?: string; type?: SuggestionType }` | `undefined`    | Optional filters                |
| `markName` | `string`                                     | `'suggestion'` | The name of the suggestion mark |

### Returns

`Suggestion[]` — Array of suggestions overlapping the range.

```js
import { findSuggestionsInRange } from '@tiptap-pro/extension-tracked-changes'

// Find all suggestions between positions 10 and 50
const suggestions = findSuggestionsInRange(editor, 10, 50)

// With filters
const userInsertions = findSuggestionsInRange(editor, 10, 50, {
  userId: 'user-123',
  type: 'add',
})
```

## suggestionExists()

Check whether a suggestion still exists in the document.

| Argument       | Type     | Default        | Description                     |
| -------------- | -------- | -------------- | ------------------------------- |
| `editor`       | `Editor` | —              | The Tiptap editor instance      |
| `suggestionId` | `string` | —              | The suggestion ID to check      |
| `markName`     | `string` | `'suggestion'` | The name of the suggestion mark |

### Returns

`boolean` — `true` if the suggestion exists.

```js
import { suggestionExists } from '@tiptap-pro/extension-tracked-changes'

if (suggestionExists(editor, 'suggestion-123')) {
  // Suggestion is still in the document
}
```

## getSuggestionRange()

Get the bounding position range of a suggestion.

| Argument       | Type     | Default        | Description                     |
| -------------- | -------- | -------------- | ------------------------------- |
| `editor`       | `Editor` | —              | The Tiptap editor instance      |
| `suggestionId` | `string` | —              | The suggestion ID               |
| `markName`     | `string` | `'suggestion'` | The name of the suggestion mark |

### Returns

`{ from: number; to: number } | null` — The bounding range, or `null` if not found.

```js
import { getSuggestionRange } from '@tiptap-pro/extension-tracked-changes'

const range = getSuggestionRange(editor, 'suggestion-123')

if (range) {
  console.log(`Suggestion spans from ${range.from} to ${range.to}`)
}
```

## acceptSuggestions()

Accept multiple suggestions by their IDs. Processes each suggestion individually.

| Argument        | Type       | Description                       |
| --------------- | ---------- | --------------------------------- |
| `editor`        | `Editor`   | The Tiptap editor instance        |
| `suggestionIds` | `string[]` | Array of suggestion IDs to accept |

### Returns

`boolean` — `true` if at least one suggestion was accepted.

```js
import { acceptSuggestions } from '@tiptap-pro/extension-tracked-changes'

acceptSuggestions(editor, ['suggestion-1', 'suggestion-2', 'suggestion-3'])
```

## rejectSuggestions()

Reject multiple suggestions by their IDs. Processes each suggestion individually.

| Argument        | Type       | Description                       |
| --------------- | ---------- | --------------------------------- |
| `editor`        | `Editor`   | The Tiptap editor instance        |
| `suggestionIds` | `string[]` | Array of suggestion IDs to reject |

### Returns

`boolean` — `true` if at least one suggestion was rejected.

```js
import { rejectSuggestions } from '@tiptap-pro/extension-tracked-changes'

rejectSuggestions(editor, ['suggestion-1', 'suggestion-2'])
```

## withoutTrackedChanges()

Returns the base document by rejecting every pending suggestion on a copy. Suggested additions are removed, suggested deletions are restored, and replacements revert to their original content. Pending formatting, attribute, node type, list nesting, and block split changes are also reverted. Node type proposals restore the original type and attributes while retaining current content after other suggestions are rejected.

| Option    | Type                    | Description                                                                                          |
| --------- | ----------------------- | ---------------------------------------------------------------------------------------------------- |
| `content` | `string \| JSONContent` | HTML or a complete document JSON object. JSON strings and content arrays are not supported.          |
| `schema`  | `Schema`                | The source document's ProseMirror schema, including `TrackedChanges` and all custom nodes and marks. |

Both options are required. The options type is exported as [`WithoutTrackedChangesOptions`](https://tiptap-docs.lin52025i.qzz.io/tracked-changes/api-reference/types.md#withouttrackedchangesoptions).

### Returns

`JSONContent` — A complete document JSON object without suggestion marks or reserved tracking attributes, including null tracking defaults. Other content, marks, and attributes are preserved as represented by the schema after rejection.

The utility leaves the input and source editor unchanged, including pending suggestions, selection, history, callbacks, comment threads, and the collaboration provider. Content without pending suggestions is supported. Repeated exports with the same content and schema produce deeply equal JSON.

```js
import { withoutTrackedChanges } from '@tiptap-pro/extension-tracked-changes'

const base = withoutTrackedChanges({
	content: editor.getJSON(),
	schema: editor.schema,
})

// HTML input also returns JSON.
const baseFromHTML = withoutTrackedChanges({
	content: editor.getHTML(),
	schema: editor.schema,
})
```

For example, a pending insertion of ` world` after `Hello` produces base JSON containing only `Hello`. The insertion stays pending in the source editor.

### Use without an editor

JSON input works without a DOM. Build the schema with `getSchema` and the same extensions used by the source document, including custom extensions.

```js
import { getSchema } from '@tiptap/core'
import StarterKit from '@tiptap/starter-kit'
import { TrackedChanges, withoutTrackedChanges } from '@tiptap-pro/extension-tracked-changes'

const schema = getSchema([StarterKit, TrackedChanges])

const base = withoutTrackedChanges({
	content: { type: 'doc', content: [{ type: 'paragraph' }] },
	schema,
})
```

### Limits and errors

HTML input requires a DOM environment and preserves text whitespace. Custom HTML renderers must retain suggestion data for the parser to restore the base content. HTML cannot restore data discarded by an earlier export or unsupported by the schema.

The utility throws an error for invalid document JSON, missing tracking schema support, or suggestions that cannot be restored. HTML input without a DOM also throws. Source content stays unchanged if export fails.

Inline atom suggestions must use node attributes. Suggestion marks on non-text nodes cause an error. Explicit list nesting deltas must be nonzero safe integers, positive for `sink` and negative for `lift`. Omitted or null deltas remain supported for older documents.

Only pending suggestions are reverted. The utility cannot undo accepted changes or edits made without tracking.

## generateSuggestionId()

Generate a unique suggestion ID. Combines timestamp with random string for uniqueness.

### Returns

`string` — A unique suggestion ID in the format `suggestion-{timestamp}-{random}`.

```js
import { generateSuggestionId } from '@tiptap-pro/extension-tracked-changes'

const id = generateSuggestionId()
// Returns: 'suggestion-1706621696789-k7x2m9p'
```

## getTimestamp()

Get the current time as an ISO 8601 timestamp string.

### Returns

`string` — Current time as ISO 8601 string.

```js
import { getTimestamp } from '@tiptap-pro/extension-tracked-changes'

const timestamp = getTimestamp()
// Returns: '2024-01-30T12:34:56.789Z'
```

## getSuggestionHTMLAttributes()

Reads suggestion data from a live ProseMirror node and returns the corresponding `data-suggestion-*` DOM attributes, safe to spread onto a NodeView's root element.

| Argument | Type   | Description                                                                 |
| -------- | ------ | --------------------------------------------------------------------------- |
| `node`   | `Node` | The ProseMirror node received as the `node` prop in your NodeView component |

### Returns

`SuggestionHTMLAttributes` — An object containing only the `data-suggestion-*` attributes derived from `node.attrs`. Keys whose value is `null` or `undefined` are omitted, so non-suggestion nodes return `{}`.

The attributes produced are:

| Output key                      | Source (`node.attrs`)                      |
| ------------------------------- | ------------------------------------------ |
| `data-suggestion-id`            | `suggestionId`                             |
| `data-suggestion-type`          | `suggestionType`                           |
| `data-suggestion-user`          | `suggestionUserId`                         |
| `data-suggestion-created`       | `suggestionCreatedAt`                      |
| `data-suggestion-updated`       | `suggestionUpdatedAt`                      |
| `data-suggestion-user-metadata` | `suggestionUserMetadata` (JSON-serialized) |
| `data-suggestion-nesting-delta` | `suggestionNestingDelta`                   |

Node type proposals additionally produce `data-suggestion-node-type-change` from `suggestionNodeTypeChange`, even when a deletion or split uses the node's primary suggestion identity. For standalone type proposals, the helper supplies the usual `attributeChange` identity attributes too.

### Why `node`, not `HTMLAttributes`

The `HTMLAttributes` prop in a NodeView is computed once at creation time and never updated. Passing it to this utility would produce stale attributes whenever suggestion state changes — for example, after a remote collaboration update or when a suggestion is accepted or rejected. The `node` prop is the reactive source of truth and is updated on every render, so always pass `node`.

### Usage

```jsx
import { getSuggestionHTMLAttributes } from '@tiptap-pro/extension-tracked-changes'

function MyNodeView({ node }) {
  const suggestionAttrs = getSuggestionHTMLAttributes(node)

  return (
    <div {...suggestionAttrs}>
      Your content here
    </div>
  )
}
```

For detailed guidance on integrating NodeViews with Tracked Changes, see the [NodeView Support guide](https://tiptap-docs.lin52025i.qzz.io/tracked-changes/guides/nodeview-support.md).
