---
title: "Customize how changes are displayed"
description: "Customize how AI Changes are displayed in the editor with custom styles and popovers."
canonical_url: "https://tiptap.dev/docs/content-ai/capabilities/changes/features/display-changes"
---

# Customize how changes are displayed

Customize how AI Changes are displayed in the editor with custom styles and popovers.

The AI Changes extension is designed with flexibility in mind. As a headless library, it gives you full control over how changes are displayed in your editor.

## Default styles

By default, the AI Changes extension applies CSS classes to highlight modified content:

- `tiptap-ai-changes--old` for deleted text
- `tiptap-ai-changes--new` for inserted text

The extension doesn't include any built-in styles, so you'll need to define your own CSS. Here's a complete example of basic styling for change highlighting:

```css
:root {
  --color-green-100: oklch(0.962 0.044 156.743);
  --color-green-700: oklch(0.527 0.154 150.069);
  --color-red-100: oklch(0.936 0.032 17.717);
  --color-red-700: oklch(0.505 0.213 27.518);
}

.tiptap-ai-changes--old,
.tiptap-ai-changes--old > * {
  color: var(--color-red-700);
  background-color: var(--color-red-100);
}

.tiptap-ai-changes--new,
.tiptap-ai-changes--new > * {
  color: var(--color-green-700);
  background-color: var(--color-green-100);
}
```

This will apply a red background to the deleted text, and a green background to the inserted text.

For more advanced styles, use the `getCustomDecorations` [configuration option](https://tiptap.dev/docs/content-ai/capabilities/changes/configure.md#custom-styles).

## Selected changes

A change is considered "selected" when the selection cursor is over it.

You can retrieve the selected change from the extension's storage object:

```ts
const storage = editor.extensionStorage.aiChanges
const selectedChange = storage.getSelectedChange()
```

To select a change programmatically, use the `selectAiChange` command.

```ts
editor.commands.selectAiChange(changeId)
```

This will move the cursor to the beginning of the change, so that it is considered "selected".

The text of the selected change can be referenced in the `getCustomDecorations` function to apply custom styles to it.

## Customize the change's appearance

The `getCustomDecorations` option allows you to control the appearance of changes and provide visual cues to the user.

It receives these arguments:

- `change`: The change object that contains the information about the change.
- `changes`: A list of all tracked changes.
- `isSelected`: A boolean that indicates if the change is selected. A change is selected when the cursor is on top of it.
- `getDefaultDecorations`: A function that returns the default decorations for the change. If the `getCustomDecorations` function is not provided, the default decorations will be used.
- `editor`: The Tiptap Editor instance.
- `previousDoc`: The previous document before the AI made changes. The changes are obtained by comparing the current document with this one.
- `currentDoc`: The document after the AI made changes.

```ts
AiChanges.configure({
  getCustomDecorations({ change, isSelected, getDefaultDecorations }) {
    // You can combine the default decorations of the AI Changes extension with your custom ones
    const decorations = getDefaultDecorations()

    // Add a custom element after the inserted text of the change
    decorations.push(
      Decoration.widget(change.newRange.to, () => {
        const element = document.createElement('span')

        element.textContent = '✅'
        return element
      }),
    )
    return decorations
  },
})
```

The custom styles and elements are implemented with the [Prosemirror Decorations API](https://prosemirror.net/docs/ref/#view.Decorations).

To learn how to show a popover when you select a change, follow [this guide](https://tiptap.dev/docs/content-ai/capabilities/changes/features/display-changes.md#show-a-popover-when-a-change-is-selected).

## Show a popover when a change is selected

In most user review workflows, you'll need to display a popover or a tooltip over a selected change, with actions to accept or reject it.

To show a popover when you select a change, use the `getCustomDecorations` option. It allows you to add custom elements to the changes, including popovers.

Below is a simplified example using React:

```tsx
// First, define a hook to store the HTML element where the popover will be rendered
const [popoverElement, setPopoverElement] = useState<HTMLElement | null>(null)

AiChanges.configure({
  getCustomDecorations({ change, isSelected, getDefaultDecorations }) {
    const decorations = getDefaultDecorations()

    // Then, create a Prosemirror decoration that contains the HTML element
    // Add a custom element after the inserted text when the change is selected
    if (isSelected) {
      decorations.push(
        Decoration.widget(change.newRange.to, () => {
          const element = document.createElement('span')

          setPopoverElement(element)
          return element
        }),
      )
    }
    return decorations
  },
})

const selectedChange = editor.extensionStorage.aiChanges.getSelectedChange()
if (popoverElement && selectedChange) {
  // Then, add the HTML content to the custom element. In this example, we use React Portals to render the popover.
  ReactDOM.createPortal(<Popover change={selectedChange} />, popoverElement)
}
```

We recommend using the [Floating UI](https://floating-ui.com/) library to display the popover. You can see an example on how to do it [in the demo](https://tiptap.dev/docs/content-ai/capabilities/changes/overview.md).

## Display changes in a sidebar outside the editor

You can access the changes data from the extension's [storage object](https://tiptap.dev/docs/content-ai/capabilities/changes/api-reference.md#extension-storage):

```ts
const storage = editor.extensionStorage.aiChanges
const changes = storage.getChanges()
```

Then, use this data to render a custom UI component. Here's an example using React:

```tsx
// Get the changes from the Editor state.
const storage = editor.extensionStorage.aichange
const changes = storage.getchanges()

// Render the changes in the UI
return (
  <div>
    {changes.map((change) => (
      <div key={change.id}>
        <button onClick={() => editor.commands.acceptAiChange(change.id)}>Accept</button>
        <button onClick={() => editor.commands.rejectAiChange(change.id)}>Reject</button>
      </div>
    ))}
  </div>
)
```

## Hide and show changes

In some scenarios, you might want to continue tracking the changes but hide them in the UI. For example, when you are streaming AI-generated content. You can hide and show changes programmatically using the `setShowAiChanges` command.

```ts
// Hide changes
editor.commands.setShowAiChanges(false)

// Show changes
editor.commands.setShowAiChanges(true)
```

This only affects the visual display of changes—the tracking mechanism continues to work in the background, and all changes remain accessible through the extension's storage methods.
