or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

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

0

# Tiptap Suggestion

1

2

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.

3

4

## Package Information

5

6

- **Package Name**: @tiptap/suggestion

7

- **Package Type**: npm

8

- **Language**: TypeScript

9

- **Installation**: `npm install @tiptap/suggestion`

10

11

## Core Imports

12

13

```typescript

14

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

15

```

16

17

For default import:

18

19

```typescript

20

import Suggestion from "@tiptap/suggestion";

21

```

22

23

For CommonJS:

24

25

```javascript

26

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

27

```

28

29

## Basic Usage

30

31

```typescript

32

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

33

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

34

35

const editor = new Editor({

36

// ... other config

37

});

38

39

// Create a suggestion plugin for mentions

40

const mentionSuggestion = Suggestion({

41

editor: editor,

42

char: '@',

43

items: ({ query }) => {

44

return [

45

{ id: 1, label: 'John Doe' },

46

{ id: 2, label: 'Jane Smith' },

47

{ id: 3, label: 'Bob Wilson' }

48

].filter(item =>

49

item.label.toLowerCase().includes(query.toLowerCase())

50

);

51

},

52

render: () => ({

53

onStart: (props) => {

54

// Create and show suggestion dropdown

55

console.log('Suggestion started', props.query);

56

},

57

onUpdate: (props) => {

58

// Update suggestion dropdown with new items

59

console.log('Suggestion updated', props.items);

60

},

61

onExit: () => {

62

// Hide suggestion dropdown

63

console.log('Suggestion exited');

64

},

65

onKeyDown: ({ event }) => {

66

// Handle keyboard navigation

67

if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {

68

return true; // Handled

69

}

70

return false; // Not handled

71

}

72

}),

73

command: ({ editor, range, props }) => {

74

// Insert the selected mention

75

editor.chain().focus().insertContentAt(range, `@${props.label}`).run();

76

},

77

});

78

79

// Add the plugin to your editor

80

editor.registerPlugin(mentionSuggestion);

81

```

82

83

## Capabilities

84

85

### Suggestion Plugin Creation

86

87

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

88

89

```typescript { .api }

90

/**

91

* Creates a suggestion plugin for Tiptap editors

92

* @param options - Configuration options for the suggestion behavior

93

* @returns ProseMirror Plugin instance

94

*/

95

function Suggestion<I = any, TSelected = any>(

96

options: SuggestionOptions<I, TSelected>

97

): Plugin<any>;

98

99

interface SuggestionOptions<I = any, TSelected = any> {

100

/** The plugin key for the suggestion plugin (default: SuggestionPluginKey) */

101

pluginKey?: PluginKey;

102

/** The editor instance (required) */

103

editor: Editor;

104

/** The character that triggers the suggestion (default: '@') */

105

char?: string;

106

/** Allow spaces in the suggestion query (default: false) */

107

allowSpaces?: boolean;

108

/** Allow the trigger character to be included in the query (default: false) */

109

allowToIncludeChar?: boolean;

110

/** Allowed prefix characters before trigger (default: [' ']) */

111

allowedPrefixes?: string[] | null;

112

/** Only match suggestions at the start of the line (default: false) */

113

startOfLine?: boolean;

114

/** HTML tag name for decoration node (default: 'span') */

115

decorationTag?: string;

116

/** CSS class for decoration node (default: 'suggestion') */

117

decorationClass?: string;

118

/** Content for decoration node (default: '') */

119

decorationContent?: string;

120

/** CSS class when decoration is empty (default: 'is-empty') */

121

decorationEmptyClass?: string;

122

/** Function called when suggestion is selected */

123

command?: (props: { editor: Editor; range: Range; props: TSelected }) => void;

124

/** Function returning suggestion items array */

125

items?: (props: { query: string; editor: Editor }) => I[] | Promise<I[]>;

126

/** Function returning render lifecycle hooks */

127

render?: () => {

128

/** Called before suggestion starts */

129

onBeforeStart?: (props: SuggestionProps<I, TSelected>) => void;

130

/** Called when suggestion starts */

131

onStart?: (props: SuggestionProps<I, TSelected>) => void;

132

/** Called before suggestion updates */

133

onBeforeUpdate?: (props: SuggestionProps<I, TSelected>) => void;

134

/** Called when suggestion updates */

135

onUpdate?: (props: SuggestionProps<I, TSelected>) => void;

136

/** Called when suggestion exits */

137

onExit?: (props: SuggestionProps<I, TSelected>) => void;

138

/** Called on keydown events, return true if handled */

139

onKeyDown?: (props: SuggestionKeyDownProps) => boolean;

140

};

141

/** Function determining if suggestion should be active */

142

allow?: (props: { editor: Editor; state: EditorState; range: Range; isActive?: boolean }) => boolean;

143

/** Custom match finding function */

144

findSuggestionMatch?: typeof findSuggestionMatch;

145

}

146

```

147

148

### Suggestion Match Finding

149

150

Finds suggestion matches in text based on configurable trigger patterns.

151

152

```typescript { .api }

153

/**

154

* Finds suggestion matches in text based on trigger configuration

155

* @param config - Trigger configuration object

156

* @returns SuggestionMatch object or null if no match found

157

*/

158

function findSuggestionMatch(config: Trigger): SuggestionMatch;

159

160

interface Trigger {

161

/** Trigger character */

162

char: string;

163

/** Whether to allow spaces in queries */

164

allowSpaces: boolean;

165

/** Whether to include trigger character in queries */

166

allowToIncludeChar: boolean;

167

/** Array of allowed prefix characters */

168

allowedPrefixes: string[] | null;

169

/** Whether to only match at line start */

170

startOfLine: boolean;

171

/** ProseMirror resolved position */

172

$position: ResolvedPos;

173

}

174

175

type SuggestionMatch = {

176

/** Range object with from/to positions */

177

range: Range;

178

/** Matched query string (excluding trigger character) */

179

query: string;

180

/** Full matched text (including trigger character) */

181

text: string;

182

} | null;

183

```

184

185

### Programmatic Exit

186

187

Programmatically exits suggestion mode.

188

189

```typescript { .api }

190

/**

191

* Programmatically exits the suggestion mode

192

* @param view - EditorView instance

193

* @param pluginKeyRef - PluginKey instance (default: SuggestionPluginKey)

194

*/

195

function exitSuggestion(view: EditorView, pluginKeyRef?: PluginKey): void;

196

```

197

198

### Default Plugin Key

199

200

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

201

202

```typescript { .api }

203

/**

204

* Default plugin key for the suggestion plugin

205

* Used when no custom pluginKey is specified in SuggestionOptions

206

*/

207

const SuggestionPluginKey: PluginKey<any>;

208

```

209

210

## Types

211

212

### Core Interfaces

213

214

```typescript { .api }

215

interface SuggestionProps<I = any, TSelected = any> {

216

/** The editor instance */

217

editor: Editor;

218

/** The range of the suggestion text */

219

range: Range;

220

/** Current query string (excluding trigger character) */

221

query: string;

222

/** Full suggestion text (including trigger character) */

223

text: string;

224

/** Array of suggestion items */

225

items: I[];

226

/** Function to execute selected suggestion */

227

command: (props: TSelected) => void;

228

/** HTML element of the decoration node */

229

decorationNode: Element | null;

230

/** Function returning DOMRect for positioning */

231

clientRect?: (() => DOMRect | null) | null;

232

}

233

234

interface SuggestionKeyDownProps {

235

/** EditorView instance */

236

view: EditorView;

237

/** KeyboardEvent */

238

event: KeyboardEvent;

239

/** Current suggestion range */

240

range: Range;

241

}

242

```

243

244

### External Dependencies

245

246

```typescript { .api }

247

// From @tiptap/core

248

interface Editor { /* Tiptap editor instance */ }

249

interface Range { from: number; to: number; }

250

251

// From @tiptap/pm/state

252

class Plugin { /* ProseMirror plugin class */ }

253

class PluginKey { /* ProseMirror plugin key class */ }

254

interface EditorState { /* ProseMirror editor state */ }

255

256

// From @tiptap/pm/view

257

interface EditorView { /* ProseMirror editor view */ }

258

259

// From @tiptap/pm/model

260

interface ResolvedPos { /* ProseMirror resolved position */ }

261

```

262

263

## Usage Examples

264

265

### Hashtag Suggestions

266

267

```typescript

268

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

269

270

const hashtagSuggestion = Suggestion({

271

editor: myEditor,

272

char: '#',

273

items: ({ query }) => {

274

return ['javascript', 'typescript', 'react', 'vue']

275

.filter(tag => tag.includes(query.toLowerCase()))

276

.map(tag => ({ tag }));

277

},

278

render: () => ({

279

onStart: (props) => {

280

// Show hashtag dropdown

281

},

282

onUpdate: (props) => {

283

// Update hashtag list

284

},

285

onExit: () => {

286

// Hide dropdown

287

}

288

}),

289

command: ({ editor, range, props }) => {

290

editor.chain().focus().insertContentAt(range, `#${props.tag}`).run();

291

}

292

});

293

```

294

295

### Slash Commands

296

297

```typescript

298

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

299

300

const slashCommandSuggestion = Suggestion({

301

editor: myEditor,

302

char: '/',

303

startOfLine: true,

304

items: ({ query }) => {

305

return [

306

{ title: 'Heading 1', command: 'heading', level: 1 },

307

{ title: 'Heading 2', command: 'heading', level: 2 },

308

{ title: 'Bullet List', command: 'bulletList' },

309

].filter(item =>

310

item.title.toLowerCase().includes(query.toLowerCase())

311

);

312

},

313

command: ({ editor, range, props }) => {

314

editor.chain().focus().deleteRange(range);

315

316

if (props.command === 'heading') {

317

editor.chain().setHeading({ level: props.level }).run();

318

} else if (props.command === 'bulletList') {

319

editor.chain().toggleBulletList().run();

320

}

321

}

322

});

323

```

324

325

### Custom Match Pattern

326

327

```typescript

328

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

329

330

const customSuggestion = Suggestion({

331

editor: myEditor,

332

char: '$',

333

allowSpaces: true,

334

allowedPrefixes: [' ', '(', '['],

335

findSuggestionMatch: (config) => {

336

// Custom matching logic

337

return findSuggestionMatch(config);

338

},

339

items: ({ query }) => {

340

// Return variable suggestions

341

return variables.filter(v => v.name.includes(query));

342

}

343

});

344

```