Types

Paid add-on

The extension exports TypeScript types for working with suggestions.

SuggestionType

Public-facing suggestion type returned by the query API:

type SuggestionType = 'add' | 'delete' | 'replace' | 'markChange'

SuggestionMarkType

Internal mark-level type stored on suggestion marks. Replace suggestions use separate mark types for their deletion and insertion parts:

type SuggestionMarkType = 'add' | 'delete' | 'replaceDeletion' | 'replaceInsertion' | 'markChange'

SuggestionMarkChangeOperation

Operation stored in a mark-change suggestion:

type SuggestionMarkChangeOperation = 'added' | 'removed'

SuggestionMarkChange

Describes one mark mutation stored on a markChange suggestion:

type SuggestionMarkChange = {
  operation: SuggestionMarkChangeOperation
  markName: string
  markAttrs: Record<string, unknown> | null
}

Suggestion

Represents a suggestion found in the document:

type Suggestion = {
  id: string
  type: SuggestionType
  userId: string
  createdAt: string
  updatedAt: string
  userMetadata: Record<string, unknown> | null
  from: number
  to: number
  text: string
  fullText: string
  // Only present for 'replace' suggestions:
  deletionRange?: { from: number; to: number }
  insertionRange?: { from: number; to: number }
  replacedText?: string
  // Only present for 'markChange' suggestions:
  markChanges?: SuggestionMarkChange[]
  // Only present when the suggestion affects block-level nodes:
  insertedNodes?: JSONContent[]
  deletedNodes?: JSONContent[]
}
  • createdAt — ISO 8601 timestamp of when the suggestion was first created
  • updatedAt — ISO 8601 timestamp of the most recent edit within the suggestion. Falls back to createdAt for legacy data that predates this field.

text vs fullText

  • text — the text authored by this suggestion alone, excluding text inserted by nested (stacked) suggestions layered on top of it. For replace suggestions, this is the new (inserted) text.
  • fullText — the complete, continuous text spanned by this suggestion's marks, including any text inserted by nested suggestions from other authors. For replace suggestions, this is the new (inserted) text.

For the vast majority of suggestions (no stacking) text === fullText. They diverge only when another author has inserted text via a nested suggestion inside this one.

Note that text only drops text that a nested suggestion inserted. A nested delete or mark-change does not insert content, so it never reduces the outer author's text. For example, if author A inserts "hello" and author B deletes "ll" inside it, A's text is still "hello" — B's deletion didn't add any text that belongs to B.

Worked example

Author A inserts "Hello world", then author B inserts "great " before "world" (nested inside A's addition):

SuggestiontextfullText
A (add, user-1)"Hello world""Hello great world"
B (add, user-2)"great ""great "
import { findSuggestions } from '@tiptap-pro/extension-tracked-changes'

const suggestions = findSuggestions(editor)
const outer = suggestions.find(s => s.userId === 'user-1')

outer.text // "Hello world"        → show this in a review sidebar
outer.fullText // "Hello great world"  → use when you need the literal span

Default to text when displaying "what this person changed" (review panels, comment threads, activity logs). Reach for fullText only when you need the exact contiguous text the mark covers in the document.

For replace suggestions:

  • text contains the new (inserted) text
  • replacedText contains the old (deleted) text
  • deletionRange and insertionRange give the exact positions of each part
  • from and to cover the full range of both parts

For markChange suggestions:

  • text contains the affected text content
  • markChanges contains the tracked mark operations, including mark names and attrs

For block-level node suggestions (e.g. inserting or deleting a paragraph or heading):

  • text always contains the actual text content of the affected node — never a [nodeName] placeholder
  • insertedNodes contains the full JSON representation of each inserted node
  • deletedNodes contains the full JSON representation of each deleted node

Use insertedNodes and deletedNodes to inspect node types and attributes when building richer UIs, such as displaying a node type badge alongside the suggestion text.

SuggestionMarkAttrs

Attributes stored on the suggestion mark:

type SuggestionMarkAttrs = {
  id: string
  type: SuggestionMarkType
  userId: string
  createdAt: string
  updatedAt: string
  userMetadata: Record<string, unknown> | null
  markChanges?: SuggestionMarkChange[] | null
}

SuggestionChangedEventPayload

Payload for the trackedChanges:suggestionChanged event. Fires whenever any content-bearing field of a suggestion changes, including range positions, text, mark changes, or type.

type SuggestionChangedEventPayload = {
  suggestionId: string
  oldSuggestion: Suggestion
  suggestion: Suggestion
  transaction: Transaction
}
  • oldSuggestion — snapshot of the suggestion before the change
  • suggestion — the current (updated) suggestion
  • transaction — the ProseMirror transaction that caused the change

SuggestionRangeChangedEventPayload

Payload for the trackedChanges:suggestionRangeChanged event. Fires only when a suggestion's document positions shift.

type SuggestionRangeChangedEventPayload = {
  suggestionId: string
  oldRange: { from: number; to: number }
  newRange: { from: number; to: number }
  suggestion: Suggestion
  transaction: Transaction
}

TrackedChangesOptions

Configuration options for the extension:

type TrackedChangesOptions = {
  enabled: boolean
  userId: string
  userMetadata?: Record<string, unknown> | null
  ignoredTrackingMarks: string[]
  onSuggestionCreate?: (suggestion: Suggestion) => void
  onSuggestionAccept?: (id: string) => void
  onSuggestionReject?: (id: string) => void
}

Type helpers

The extension exports constants and helper functions for working with suggestion types:

import {
  SUGGESTION_TYPES,
  isAddLikeType,
  isDeleteLikeType,
  isReplaceMarkType,
  markTypeToSuggestionType,
} from '@tiptap-pro/extension-tracked-changes'

// Constants
SUGGESTION_TYPES.ADD              // 'add'
SUGGESTION_TYPES.DELETE           // 'delete'
SUGGESTION_TYPES.REPLACE_DELETION  // 'replaceDeletion'
SUGGESTION_TYPES.REPLACE_INSERTION // 'replaceInsertion'
SUGGESTION_TYPES.MARK_CHANGE      // 'markChange'

// Check if a mark type represents added content (matches 'add' and 'replaceInsertion')
isAddLikeType('add') // true
isAddLikeType('replaceInsertion') // true

// Check if a mark type represents deleted content (matches 'delete' and 'replaceDeletion')
isDeleteLikeType('delete') // true
isDeleteLikeType('replaceDeletion') // true

// Check if a mark type is one of the replace sub-types
isReplaceMarkType('replaceDeletion') // true

// Convert a mark-level type to the public-facing suggestion type
markTypeToSuggestionType('replaceDeletion') // 'replace'
markTypeToSuggestionType('add') // 'add'