or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-tiptap--suggestion

Suggestion plugin for Tiptap that provides triggered autocomplete functionality for mentions, hashtags, and other contextual suggestions

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@tiptap/suggestion@3.4.x

To install, run

npx @tessl/cli install tessl/npm-tiptap--suggestion@3.4.0

index.mddocs/

Tiptap Suggestion

Tiptap Suggestion is a utility plugin for Tiptap editors that enables triggered autocomplete functionality such as mentions, hashtags, slash commands, and other contextual suggestions. It provides a flexible ProseMirror plugin that detects trigger characters and offers lifecycle hooks for rendering custom suggestion interfaces.

Package Information

  • Package Name: @tiptap/suggestion
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @tiptap/suggestion

Core Imports

import { Suggestion, exitSuggestion, findSuggestionMatch, SuggestionPluginKey } from "@tiptap/suggestion";

For default import:

import Suggestion from "@tiptap/suggestion";

For CommonJS:

const { Suggestion, exitSuggestion, findSuggestionMatch, SuggestionPluginKey } = require("@tiptap/suggestion");

Basic Usage

import { Editor } from "@tiptap/core";
import { Suggestion } from "@tiptap/suggestion";

const editor = new Editor({
  // ... other config
});

// Create a suggestion plugin for mentions
const mentionSuggestion = Suggestion({
  editor: editor,
  char: '@',
  items: ({ query }) => {
    return [
      { id: 1, label: 'John Doe' },
      { id: 2, label: 'Jane Smith' },
      { id: 3, label: 'Bob Wilson' }
    ].filter(item => 
      item.label.toLowerCase().includes(query.toLowerCase())
    );
  },
  render: () => ({
    onStart: (props) => {
      // Create and show suggestion dropdown
      console.log('Suggestion started', props.query);
    },
    onUpdate: (props) => {
      // Update suggestion dropdown with new items
      console.log('Suggestion updated', props.items);
    },
    onExit: () => {
      // Hide suggestion dropdown
      console.log('Suggestion exited');
    },
    onKeyDown: ({ event }) => {
      // Handle keyboard navigation
      if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
        return true; // Handled
      }
      return false; // Not handled
    }
  }),
  command: ({ editor, range, props }) => {
    // Insert the selected mention
    editor.chain().focus().insertContentAt(range, `@${props.label}`).run();
  },
});

// Add the plugin to your editor
editor.registerPlugin(mentionSuggestion);

Capabilities

Suggestion Plugin Creation

Creates a ProseMirror plugin that handles suggestion functionality with customizable behavior and rendering.

/**
 * Creates a suggestion plugin for Tiptap editors
 * @param options - Configuration options for the suggestion behavior
 * @returns ProseMirror Plugin instance
 */
function Suggestion<I = any, TSelected = any>(
  options: SuggestionOptions<I, TSelected>
): Plugin<any>;

interface SuggestionOptions<I = any, TSelected = any> {
  /** The plugin key for the suggestion plugin (default: SuggestionPluginKey) */
  pluginKey?: PluginKey;
  /** The editor instance (required) */
  editor: Editor;
  /** The character that triggers the suggestion (default: '@') */
  char?: string;
  /** Allow spaces in the suggestion query (default: false) */
  allowSpaces?: boolean;
  /** Allow the trigger character to be included in the query (default: false) */
  allowToIncludeChar?: boolean;
  /** Allowed prefix characters before trigger (default: [' ']) */
  allowedPrefixes?: string[] | null;
  /** Only match suggestions at the start of the line (default: false) */
  startOfLine?: boolean;
  /** HTML tag name for decoration node (default: 'span') */
  decorationTag?: string;
  /** CSS class for decoration node (default: 'suggestion') */
  decorationClass?: string;
  /** Content for decoration node (default: '') */
  decorationContent?: string;
  /** CSS class when decoration is empty (default: 'is-empty') */
  decorationEmptyClass?: string;
  /** Function called when suggestion is selected */
  command?: (props: { editor: Editor; range: Range; props: TSelected }) => void;
  /** Function returning suggestion items array */
  items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>;
  /** Function returning render lifecycle hooks */
  render?: () => {
    /** Called before suggestion starts */
    onBeforeStart?: (props: SuggestionProps<I, TSelected>) => void;
    /** Called when suggestion starts */
    onStart?: (props: SuggestionProps<I, TSelected>) => void;
    /** Called before suggestion updates */
    onBeforeUpdate?: (props: SuggestionProps<I, TSelected>) => void;
    /** Called when suggestion updates */
    onUpdate?: (props: SuggestionProps<I, TSelected>) => void;
    /** Called when suggestion exits */
    onExit?: (props: SuggestionProps<I, TSelected>) => void;
    /** Called on keydown events, return true if handled */
    onKeyDown?: (props: SuggestionKeyDownProps) => boolean;
  };
  /** Function determining if suggestion should be active */
  allow?: (props: { editor: Editor; state: EditorState; range: Range; isActive?: boolean }) => boolean;
  /** Custom match finding function */
  findSuggestionMatch?: typeof findSuggestionMatch;
}

Suggestion Match Finding

Finds suggestion matches in text based on configurable trigger patterns.

/**
 * Finds suggestion matches in text based on trigger configuration
 * @param config - Trigger configuration object
 * @returns SuggestionMatch object or null if no match found
 */
function findSuggestionMatch(config: Trigger): SuggestionMatch;

interface Trigger {
  /** Trigger character */
  char: string;
  /** Whether to allow spaces in queries */
  allowSpaces: boolean;
  /** Whether to include trigger character in queries */
  allowToIncludeChar: boolean;
  /** Array of allowed prefix characters */
  allowedPrefixes: string[] | null;
  /** Whether to only match at line start */
  startOfLine: boolean;
  /** ProseMirror resolved position */
  $position: ResolvedPos;
}

type SuggestionMatch = {
  /** Range object with from/to positions */
  range: Range;
  /** Matched query string (excluding trigger character) */
  query: string;
  /** Full matched text (including trigger character) */
  text: string;
} | null;

Programmatic Exit

Programmatically exits suggestion mode.

/**
 * Programmatically exits the suggestion mode
 * @param view - EditorView instance
 * @param pluginKeyRef - PluginKey instance (default: SuggestionPluginKey)
 */
function exitSuggestion(view: EditorView, pluginKeyRef?: PluginKey): void;

Default Plugin Key

Default PluginKey instance used by the suggestion plugin when no custom pluginKey is provided.

/**
 * Default plugin key for the suggestion plugin
 * Used when no custom pluginKey is specified in SuggestionOptions
 */
const SuggestionPluginKey: PluginKey<any>;

Types

Core Interfaces

interface SuggestionProps<I = any, TSelected = any> {
  /** The editor instance */
  editor: Editor;
  /** The range of the suggestion text */
  range: Range;
  /** Current query string (excluding trigger character) */
  query: string;
  /** Full suggestion text (including trigger character) */
  text: string;
  /** Array of suggestion items */
  items: I[];
  /** Function to execute selected suggestion */
  command: (props: TSelected) => void;
  /** HTML element of the decoration node */
  decorationNode: Element | null;
  /** Function returning DOMRect for positioning */
  clientRect?: (() => DOMRect | null) | null;
}

interface SuggestionKeyDownProps {
  /** EditorView instance */
  view: EditorView;
  /** KeyboardEvent */
  event: KeyboardEvent;
  /** Current suggestion range */
  range: Range;
}

External Dependencies

// From @tiptap/core
interface Editor { /* Tiptap editor instance */ }
interface Range { from: number; to: number; }

// From @tiptap/pm/state
class Plugin { /* ProseMirror plugin class */ }
class PluginKey { /* ProseMirror plugin key class */ }
interface EditorState { /* ProseMirror editor state */ }

// From @tiptap/pm/view
interface EditorView { /* ProseMirror editor view */ }

// From @tiptap/pm/model
interface ResolvedPos { /* ProseMirror resolved position */ }

Usage Examples

Hashtag Suggestions

import { Suggestion } from "@tiptap/suggestion";

const hashtagSuggestion = Suggestion({
  editor: myEditor,
  char: '#',
  items: ({ query }) => {
    return ['javascript', 'typescript', 'react', 'vue']
      .filter(tag => tag.includes(query.toLowerCase()))
      .map(tag => ({ tag }));
  },
  render: () => ({
    onStart: (props) => {
      // Show hashtag dropdown
    },
    onUpdate: (props) => {
      // Update hashtag list
    },
    onExit: () => {
      // Hide dropdown
    }
  }),
  command: ({ editor, range, props }) => {
    editor.chain().focus().insertContentAt(range, `#${props.tag}`).run();
  }
});

Slash Commands

import { Suggestion } from "@tiptap/suggestion";

const slashCommandSuggestion = Suggestion({
  editor: myEditor,
  char: '/',
  startOfLine: true,
  items: ({ query }) => {
    return [
      { title: 'Heading 1', command: 'heading', level: 1 },
      { title: 'Heading 2', command: 'heading', level: 2 },
      { title: 'Bullet List', command: 'bulletList' },
    ].filter(item => 
      item.title.toLowerCase().includes(query.toLowerCase())
    );
  },
  command: ({ editor, range, props }) => {
    editor.chain().focus().deleteRange(range);
    
    if (props.command === 'heading') {
      editor.chain().setHeading({ level: props.level }).run();
    } else if (props.command === 'bulletList') {
      editor.chain().toggleBulletList().run();
    }
  }
});

Custom Match Pattern

import { Suggestion, findSuggestionMatch } from "@tiptap/suggestion";

const customSuggestion = Suggestion({
  editor: myEditor,
  char: '$',
  allowSpaces: true,
  allowedPrefixes: [' ', '(', '['],
  findSuggestionMatch: (config) => {
    // Custom matching logic
    return findSuggestionMatch(config);
  },
  items: ({ query }) => {
    // Return variable suggestions
    return variables.filter(v => v.name.includes(query));
  }
});