Customize how suggestions are displayed

The AI Suggestion extension is headless and fully customizable. This means that you have full control over how suggestions are displayed in the editor.

Show Loading/Error States When Loading Suggestions

You can access the extension's current loading state by reading its extension storage object

const storage = editor.extensionStorage.aiSuggestion
if (storage.isLoading) {
  // Show a loading spinner
} else if (storage.error) {
  // Show an error message.
}

The storage.error property will contain the error object that was thrown while loading suggestions. You can use this object to display different error messages depending on the error type.

Default Suggestion Styles

By default, the AI Suggestion extension will apply the CSS class tiptap-ai-suggestion to each suggestion. It will also add a style attribute with these color variables: --tiptap-ai-suggestion-color and --tiptap-ai-suggestion-background-color. The tiptap-ai-suggestion class can be used to apply simple styles to the suggestions in the editor.

.tiptap-ai-suggestion {
  border-bottom: 2px solid var(--tiptap-ai-suggestion-color);
  margin-bottom: -2px;
}

For more advanced styles, use the getCustomSuggestionDecoration configuration option.

Selected Suggestions

A suggestion is considered "selected" when the cursor is over it.

You can read the selected suggestion from the extension's storage object

const storage = editor.extensionStorage.aiSuggestion
const selectedSuggestion = storage.getSelectedSuggestion()

To select a suggestion programmatically, use the selectAiSuggestion command.

editor.commands.selectAiSuggestion(suggestionId)

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

By default, the AI Suggestion extension will apply the CSS class tiptap-ai-suggestion--selected to the selected suggestion. This class can be used to style the selected suggestion in the editor.

.tiptap-ai-suggestion--selected {
  background-color: var(--tiptap-ai-suggestion-background-color);
  transition: background-color 0.5s;
}

Customize the Suggestion's Appearance

The getCustomSuggestionDecoration option allows you to control the appearance of suggestions and provide visual cues to the user. You can add custom CSS classes to the suggestions, and add custom elements before/after them. This is useful for adding popovers, tooltips, icons, or other elements to the suggestions.

The function Custom suggestion styles are applied using the Prosemirror Decorations API.

To learn how to show a popover when you select a suggestion, follow this guide.

AiSuggestion.configure({
  getCustomSuggestionDecoration({ suggestion, isSelected, getDefaultDecorations }) {
    // You can combine the default decorations of the AI Suggestion extension with your custom ones
    const decorations = getDefaultDecorations()

    // Add a custom element after the suggestion text
    Decoration.widget(suggestion.deleteRange.to, () => {
      const element = document.createElement('span')
      element.textContent = '⚠️'
      return element
    })
    return decorations
  },
})

Show a Popover When You Select a Suggestion

An essential feature of modern AI Suggestions is the ability to show a popover or a tooltip when you select a suggestion. This popover usually provides extra information about the suggestion, and allows the user to accept or reject it.

To show a popover when you select a suggestion, you need to use the getCustomSuggestionDecoration option. This function allows you to add custom elements to the suggestions, including popovers.

Below is a simplified example on how to do it with the React UI library.

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

AiSuggestion.configure({
  getCustomSuggestionDecoration({ suggestion, isSelected, getDefaultDecorations }) {
    const decorations = getDefaultDecorations()

    // Then, create a Prosemirror decoration that contains the HTML element
    Decoration.widget(suggestion.deleteRange.to, () => {
      const element = document.createElement('span')

      return element
    })
    return decorations
  },
})

// Then, add the content to the custom element. In this example, we use React Portals to render the popover inside the editor.
if (popoverElement) {
  ReactDOM.createPortal(<Popover suggestion={editor.selectedSuggestion} />, popoverElement)
}

We recommend using the Floating UI library to display the popover. You can see an example on how to do it in the demo.

When you render the suggestion in the popover, you might want to show the previous and next words of the sentence to give the user more context. We've created the getNextWord and getPreviousWord utility functions so that you don’t have to implement them yourself. You can import them from the @tiptap-pro/extension-ai-suggestion library.

import { getNextWord, getPreviousWord } from '@tiptap-pro/extension-ai-suggestion'

// Get the previous word in the sentence.
const { previousWord } = getPreviousWord(editor, suggestion.deleteRange.from)
// Get the next word in the sentence and the punctuation mark that follows it, if it's the end of the sentence.
const { nextWord, punctuationMark } = getNextWord(editor, suggestion.deleteRange.to)

Display Suggestions in a Sidebar Outside the Editor

You can access the current suggestions from the extension's storage object.

const storage = editor.extensionStorage.aiSuggestion
const suggestions = storage.getSuggestions()

Then, you can use this data to render suggestions in the UI, outside the editor. Here is an example of how to do it with the React UI library

// Get the suggestions from the Editor state.
const storage = editor.extensionStorage.aiSuggestion
const suggestions = storage.getSuggestions()

// Render the suggestions in the UI
return (
  <div>
    {suggestions.map((suggestion) => (
      <div key={suggestion.id}>
        <p>{suggestion.deleteText}</p>
        <ul>
          {suggestion.replacementOptions.map((option) => (
            <li key={option.id}>{option.addText}</li>
          ))}
        </ul>
      </div>
    ))}
  </div>
)