Styling

Compare renders its default diff UI with CSS classes. Add your own class or attributes with displayOptions when calling a command like compareDocuments or compareVersions.

editor.commands.compareDocuments({
  docA,
  docB,
  displayOptions: {
    attributes: { class: 'my-added-content' },
    replacementAttributes: { class: 'my-removed-content' },
  },
})

Default CSS classes

Every diff suggestion renders the current document content and the suggested replacement content. The following classes are applied to those elements.

Current content

  • .diff-suggestion — applied to every current-content element.
  • .diff-suggestion--selected — applied when the suggestion is selected.
  • .diff-suggestion--change-group — applied when the suggestion contains grouped inline changes.

If the suggestion is created from a change group and it has sub-changes, the sub-changes contain these classes:

  • .diff-suggestion-sub-change — applied to each individual change within a group.
  • .diff-suggestion-sub-change--selected — applied to a current-content sub-change when its suggestion is selected.

Nested changes are displayed in the current-content side of the review and have these classes:

  • .diff-suggestion-nested-change — applied to every nested change.
  • .diff-suggestion-nested-change--inline — applied when the nested change covers inline content.
  • .diff-suggestion-nested-change--block — applied when the nested change covers block content.
  • .diff-suggestion-nested-change--selected — applied when the parent suggestion is selected.

Use displayOptions.getNestedChangeAttributes to add attributes to a nested change. A returned class is appended to these default classes.

Suggested content

  • .diff-suggestion-diff — applied to every suggested-content element.
  • .diff-suggestion-diff--selected — applied when the suggestion is selected.
  • .diff-suggestion-diff--change-group — applied when the suggestion contains grouped inline changes.

If the suggestion is created from a change group and it has sub-changes, the sub-changes contain these classes:

  • .diff-suggestion-diff-sub-change — applied to each individual suggested-content change within a group.
  • .diff-suggestion-diff-sub-change--selected — applied to a suggested-content sub-change when its suggestion is selected.

Example CSS styles

The Compare documents guide includes example CSS stylesheets that work out of the box with the default suggestions that are rendered by the Compare extension.

The Compare versions guide includes a CSS stylesheet for a diff view where changes are marked with a different color depending on their author.

Custom suggestion rendering

The renderDecorations option gives you full control over how the suggestion is rendered. It's a function that receives a DiffSuggestion object and returns a list of ProseMirror decorations.

editor.commands.compareDocuments({
  docA,
  docB,
  displayOptions: {
    renderDecorations({ suggestion }) {
      return [
        Decoration.inline(suggestion.range.from, suggestion.range.to, {
          class: 'custom-decoration',
        }),
      ]
    },
  },
})

To combine your custom decorations with the default decorations displayed by the Compare extension, use the defaultRenderDecorations option:

editor.commands.compareDocuments({
  docA,
  docB,
  displayOptions: {
    renderDecorations({ suggestion, isSelected, defaultRenderDecorations }) {
      return [
        Decoration.inline(suggestion.range.from, suggestion.range.to, {
          class: 'custom-decoration',
        }),
        ...defaultRenderDecorations({
          attributes: isSelected
            ? {
                class: 'custom-selected-class',
              }
            : {},
        }),
      ]
    },
  },
})

Render custom Node Views

To apply different styling to a Tiptap Node View when it's inside a diff suggestion, use the getSuggestionNodeViewContext utility.

The function returns useful context about the suggestion. Learn more about it in the API Reference.

import { getSuggestionNodeViewContext } from '@tiptap-pro/compare'
import Heading from '@tiptap/extension-heading'
import { NodeViewWrapper, ReactNodeViewRenderer } from '@tiptap/react'

const CustomHeading = Heading.extend({
  addNodeView() {
    return ReactNodeViewRenderer(({ decorations }) => {
      const { suggestion, isSelected, isReplacement } = getSuggestionNodeViewContext(decorations)

      return (
        <NodeViewWrapper style={{ position: 'relative' }}>
          {suggestion && (
            <span>
              Suggestion {suggestion.id} is {isSelected ? 'selected' : 'not selected'} and rendered
              in the {isReplacement ? 'replacement' : 'document'}.
            </span>
          )}
        </NodeViewWrapper>
      )
    })
  },
})