0
# Editor Core
1
2
The Editor class is the central component of @tiptap/core, managing the editor state, view, extensions, and providing the main API interface for interacting with the rich text editor.
3
4
## Capabilities
5
6
### Editor Constructor
7
8
Creates a new editor instance with the specified configuration options.
9
10
```typescript { .api }
11
/**
12
* Creates a new Tiptap editor instance
13
* @param options - Configuration options for the editor
14
*/
15
constructor(options: Partial<EditorOptions>): Editor;
16
17
interface EditorOptions {
18
/** The DOM element to mount the editor to */
19
element?: Element;
20
21
/** Initial content for the editor */
22
content?: Content;
23
24
/** Array of extensions to load */
25
extensions?: Extensions;
26
27
/** Whether to inject default CSS styles */
28
injectCSS?: boolean;
29
30
/** Initial focus position */
31
autofocus?: FocusPosition;
32
33
/** Whether the editor should be editable */
34
editable?: boolean;
35
36
/** Enable input rules (markdown-like shortcuts) */
37
enableInputRules?: boolean;
38
39
/** Enable paste rules */
40
enablePasteRules?: boolean;
41
42
/** Enable core extensions */
43
enableCoreExtensions?: boolean;
44
45
/** Callback fired before editor creation */
46
onBeforeCreate?(props: EditorEvents['beforeCreate']): void;
47
48
/** Callback fired after editor creation */
49
onCreate?(props: EditorEvents['create']): void;
50
51
/** Callback fired on content updates */
52
onUpdate?(props: EditorEvents['update']): void;
53
54
/** Callback fired on selection updates */
55
onSelectionUpdate?(props: EditorEvents['selectionUpdate']): void;
56
57
/** Callback fired on transactions */
58
onTransaction?(props: EditorEvents['transaction']): void;
59
60
/** Callback fired on focus */
61
onFocus?(props: EditorEvents['focus']): void;
62
63
/** Callback fired on blur */
64
onBlur?(props: EditorEvents['blur']): void;
65
66
/** Callback fired when editor is destroyed */
67
onDestroy?(): void;
68
}
69
70
type Content =
71
| string
72
| JSONContent
73
| JSONContent[]
74
| ProseMirrorNode
75
| null;
76
77
type FocusPosition =
78
| boolean
79
| 'start'
80
| 'end'
81
| 'all'
82
| number
83
| { from: number; to?: number };
84
```
85
86
**Usage Examples:**
87
88
```typescript
89
import { Editor } from '@tiptap/core';
90
91
// Basic editor
92
const editor = new Editor({
93
element: document.querySelector('#editor'),
94
content: '<p>Hello World!</p>'
95
});
96
97
// Editor with callbacks
98
const editor = new Editor({
99
element: document.querySelector('#editor'),
100
content: '<p>Hello World!</p>',
101
autofocus: 'end',
102
editable: true,
103
onUpdate: ({ editor }) => {
104
console.log('Content updated:', editor.getHTML());
105
},
106
onFocus: ({ editor, event }) => {
107
console.log('Editor focused');
108
}
109
});
110
```
111
112
### Core Properties
113
114
Essential properties for inspecting the editor state and configuration.
115
116
```typescript { .api }
117
/** ProseMirror EditorView instance (proxied when unmounted) */
118
readonly view: EditorView;
119
120
/** Current ProseMirror EditorState */
121
readonly state: EditorState;
122
123
/** ProseMirror Schema built from extensions */
124
readonly schema: Schema;
125
126
/** Storage object shared between extensions */
127
readonly storage: Storage;
128
129
/** Whether the editor accepts input */
130
readonly isEditable: boolean;
131
132
/** Whether the editor currently has focus */
133
readonly isFocused: boolean;
134
135
/** Whether the editor content is empty */
136
readonly isEmpty: boolean;
137
138
/** Whether the editor has been destroyed */
139
readonly isDestroyed: boolean;
140
141
/** Whether the editor is fully initialized */
142
readonly isInitialized: boolean;
143
```
144
145
### Lifecycle Management
146
147
Methods for managing the editor lifecycle - mounting, unmounting, and destroying.
148
149
```typescript { .api }
150
/**
151
* Mount the editor to a DOM element
152
* @param element - Optional DOM element to mount to (uses constructor element if not provided)
153
* @returns The editor instance for chaining
154
*/
155
mount(element?: Element): Editor;
156
157
/**
158
* Unmount the editor from DOM while preserving the instance for potential remounting
159
* @returns The editor instance for chaining
160
*/
161
unmount(): Editor;
162
163
/**
164
* Permanently destroy the editor instance and clean up resources
165
*/
166
destroy(): void;
167
```
168
169
**Usage Examples:**
170
171
```typescript
172
// Create editor without mounting
173
const editor = new Editor({
174
content: '<p>Hello World!</p>'
175
});
176
177
// Mount later
178
editor.mount(document.querySelector('#editor'));
179
180
// Temporarily unmount (can be remounted)
181
editor.unmount();
182
183
// Mount to different element
184
editor.mount(document.querySelector('#another-editor'));
185
186
// Permanently destroy
187
editor.destroy();
188
```
189
190
### Command Execution
191
192
Core command execution interface providing single commands, command chains, and command validation.
193
194
```typescript { .api }
195
/** Access to all registered commands for single execution */
196
readonly commands: SingleCommands;
197
198
/**
199
* Create a command chain for executing multiple commands in sequence
200
* @returns ChainedCommands instance
201
*/
202
chain(): ChainedCommands;
203
204
/**
205
* Create a command validation interface
206
* @returns CanCommands instance for testing command executability
207
*/
208
can(): CanCommands;
209
210
interface SingleCommands {
211
[commandName: string]: (attributes?: Record<string, any>) => boolean;
212
}
213
214
interface ChainedCommands {
215
[commandName: string]: (attributes?: Record<string, any>) => ChainedCommands;
216
/** Execute the command chain */
217
run(): boolean;
218
}
219
220
interface CanCommands {
221
[commandName: string]: (attributes?: Record<string, any>) => boolean;
222
}
223
```
224
225
**Usage Examples:**
226
227
```typescript
228
// Single command execution
229
const success = editor.commands.setBold();
230
231
// Command chaining
232
editor
233
.chain()
234
.focus()
235
.setBold()
236
.setItalic()
237
.insertContent('Bold and italic text')
238
.run();
239
240
// Command validation
241
if (editor.can().setBold()) {
242
editor.commands.setBold();
243
}
244
245
// Conditional chaining
246
editor
247
.chain()
248
.focus()
249
.toggleBold()
250
.insertContent(editor.can().setItalic() ? 'Can italicize' : 'Cannot italicize')
251
.run();
252
```
253
254
### Content Management
255
256
Methods for retrieving and setting editor content in various formats.
257
258
```typescript { .api }
259
/**
260
* Get editor content as HTML string
261
* @returns HTML representation of the document
262
*/
263
getHTML(): string;
264
265
/**
266
* Get editor content as JSON
267
* @returns JSONContent representation of the document
268
*/
269
getJSON(): JSONContent;
270
271
/**
272
* Get editor content as plain text
273
* @param options - Text extraction options
274
* @returns Plain text content
275
*/
276
getText(options?: GetTextOptions): string;
277
278
interface GetTextOptions {
279
/** Include block separators (default: true) */
280
blockSeparator?: string;
281
/** Include text between given positions */
282
from?: number;
283
to?: number;
284
}
285
286
interface JSONContent {
287
type?: string;
288
attrs?: Record<string, any>;
289
content?: JSONContent[];
290
marks?: Array<{
291
type: string;
292
attrs?: Record<string, any>;
293
}>;
294
text?: string;
295
}
296
```
297
298
**Usage Examples:**
299
300
```typescript
301
// Get content in different formats
302
const htmlContent = editor.getHTML();
303
// '<p><strong>Bold text</strong></p>'
304
305
const jsonContent = editor.getJSON();
306
// { type: 'doc', content: [{ type: 'paragraph', content: [...] }] }
307
308
const plainText = editor.getText();
309
// 'Bold text'
310
311
const textWithCustomSeparator = editor.getText({
312
blockSeparator: ' | '
313
});
314
315
// Get text from specific range
316
const rangeText = editor.getText({
317
from: 0,
318
to: 10
319
});
320
```
321
322
### Configuration Management
323
324
Methods for updating editor configuration and behavior at runtime.
325
326
```typescript { .api }
327
/**
328
* Update editor options
329
* @param options - Partial options to update
330
*/
331
setOptions(options: Partial<EditorOptions>): void;
332
333
/**
334
* Change the editable state of the editor
335
* @param editable - Whether the editor should be editable
336
*/
337
setEditable(editable: boolean): void;
338
339
/**
340
* Register a new ProseMirror plugin
341
* @param plugin - ProseMirror plugin to register
342
* @param handlePlugins - Optional function to handle plugin conflicts
343
*/
344
registerPlugin(
345
plugin: Plugin,
346
handlePlugins?: (newPlugin: Plugin, existingPlugins: Plugin[]) => Plugin[]
347
): void;
348
349
/**
350
* Unregister a ProseMirror plugin
351
* @param nameOrKey - Plugin name or PluginKey to remove
352
*/
353
unregisterPlugin(nameOrKey: string | PluginKey): void;
354
```
355
356
**Usage Examples:**
357
358
```typescript
359
// Update editor options
360
editor.setOptions({
361
editable: false,
362
onUpdate: ({ editor }) => {
363
console.log('New update handler');
364
}
365
});
366
367
// Toggle editable state
368
editor.setEditable(false); // Make read-only
369
editor.setEditable(true); // Make editable again
370
371
// Register custom plugin
372
import { Plugin, PluginKey } from '@tiptap/pm/state';
373
374
const customPluginKey = new PluginKey('customPlugin');
375
const customPlugin = new Plugin({
376
key: customPluginKey,
377
// plugin implementation
378
});
379
380
editor.registerPlugin(customPlugin);
381
382
// Unregister plugin
383
editor.unregisterPlugin('customPlugin');
384
// or
385
editor.unregisterPlugin(customPluginKey);
386
```
387
388
### State Inspection
389
390
Methods for inspecting the current editor state and selection.
391
392
```typescript { .api }
393
/**
394
* Get attributes of the current selection
395
* @param nameOrType - Node/mark name, NodeType, or MarkType
396
* @returns Attributes object
397
*/
398
getAttributes(nameOrType: string | NodeType | MarkType): Record<string, any>;
399
400
/**
401
* Check if a node or mark is active in the current selection
402
* @param name - Node or mark name to check
403
* @param attributes - Optional attributes to match
404
* @returns Whether the node/mark is active
405
*/
406
isActive(name: string, attributes?: Record<string, any>): boolean;
407
```
408
409
**Usage Examples:**
410
411
```typescript
412
// Check if formatting is active
413
const isBold = editor.isActive('bold');
414
const isItalic = editor.isActive('italic');
415
const isHeading = editor.isActive('heading', { level: 1 });
416
417
// Get current attributes
418
const linkAttrs = editor.getAttributes('link');
419
// { href: 'https://example.com', target: '_blank' }
420
421
const headingAttrs = editor.getAttributes('heading');
422
// { level: 2 }
423
424
// Use in UI state
425
function BoldButton() {
426
const isActive = editor.isActive('bold');
427
return (
428
<button
429
className={isActive ? 'active' : ''}
430
onClick={() => editor.chain().focus().toggleBold().run()}
431
>
432
Bold
433
</button>
434
);
435
}
436
```
437
438
### Content Querying
439
440
Advanced methods for querying and navigating the document structure.
441
442
```typescript { .api }
443
/**
444
* Create a NodePos instance at the specified position
445
* @param pos - Document position
446
* @returns NodePos for advanced position operations
447
*/
448
$pos(pos: number): NodePos;
449
450
/**
451
* Find a single node matching the selector and attributes
452
* @param selector - Node type selector
453
* @param attributes - Optional attributes to match
454
* @returns NodePos if found, null otherwise
455
*/
456
$node(selector: string, attributes?: Record<string, any>): NodePos | null;
457
458
/**
459
* Find all nodes matching the selector and attributes
460
* @param selector - Node type selector
461
* @param attributes - Optional attributes to match
462
* @returns Array of NodePos instances
463
*/
464
$nodes(selector: string, attributes?: Record<string, any>): NodePos[];
465
```
466
467
**Usage Examples:**
468
469
```typescript
470
// Get position info
471
const pos = editor.$pos(10);
472
console.log(pos.node()); // Node at position
473
console.log(pos.parent); // Parent node
474
475
// Find specific nodes
476
const firstHeading = editor.$node('heading');
477
const h1 = editor.$node('heading', { level: 1 });
478
479
// Find all nodes of a type
480
const allParagraphs = editor.$nodes('paragraph');
481
const allImages = editor.$nodes('image');
482
483
// Advanced querying
484
const allH2WithClass = editor.$nodes('heading', {
485
level: 2,
486
class: 'special'
487
});
488
489
// Navigate and modify
490
const heading = editor.$node('heading');
491
if (heading) {
492
heading.content = 'Updated heading content';
493
heading.setAttribute({ level: 2 });
494
}
495
```
496
497
### NodePos Class
498
499
The NodePos class provides a DOM-like interface for navigating and manipulating nodes in the document. It offers powerful querying and modification capabilities similar to browser DOM APIs.
500
501
```typescript { .api }
502
/**
503
* Represents a position in the document with DOM-like navigation and manipulation capabilities
504
*/
505
class NodePos {
506
/** The ProseMirror node at this position */
507
readonly node: Node;
508
509
/** The document position */
510
readonly pos: number;
511
512
/** The depth in the document tree */
513
readonly depth: number;
514
515
/** Node attributes */
516
readonly attributes: Record<string, any>;
517
518
/** Node text content */
519
readonly textContent: string;
520
521
/** Node size in the document */
522
readonly size: number;
523
524
/** Start position of the node */
525
readonly from: number;
526
527
/** End position of the node */
528
readonly to: number;
529
530
/** Node range (from/to positions) */
531
readonly range: Range;
532
533
/** Corresponding DOM element */
534
readonly element: HTMLElement;
535
536
/** Node content fragment */
537
content: Fragment;
538
539
// Navigation properties
540
/** Parent node (null if at root) */
541
readonly parent: NodePos | null;
542
543
/** Previous sibling node */
544
readonly before: NodePos | null;
545
546
/** Next sibling node */
547
readonly after: NodePos | null;
548
549
/** Child nodes array */
550
readonly children: NodePos[];
551
552
/** First child node */
553
readonly firstChild: NodePos | null;
554
555
/** Last child node */
556
readonly lastChild: NodePos | null;
557
558
// Query methods
559
/**
560
* Find the closest ancestor matching the selector
561
* @param selector - Node type to search for
562
* @param attributes - Optional attributes to match
563
* @returns Closest matching ancestor or null
564
*/
565
closest(selector: string, attributes?: Record<string, any>): NodePos | null;
566
567
/**
568
* Find first descendant matching the selector
569
* @param selector - Node type to search for
570
* @param attributes - Optional attributes to match
571
* @returns First matching descendant or null
572
*/
573
querySelector(selector: string, attributes?: Record<string, any>): NodePos | null;
574
575
/**
576
* Find all descendants matching the selector
577
* @param selector - Node type to search for
578
* @param attributes - Optional attributes to match
579
* @returns Array of matching descendants
580
*/
581
querySelectorAll(selector: string, attributes?: Record<string, any>): NodePos[];
582
583
// Modification methods
584
/**
585
* Set attributes on this node
586
* @param attributes - Attributes to set
587
*/
588
setAttribute(attributes: Record<string, any>): void;
589
}
590
591
interface Range {
592
from: number;
593
to: number;
594
}
595
```
596
597
**Usage Examples:**
598
599
```typescript
600
// DOM-like navigation
601
const pos = editor.$pos(50);
602
const parent = pos.parent;
603
const children = pos.children;
604
const firstChild = pos.firstChild;
605
606
// Query for specific nodes
607
const heading = pos.querySelector('heading');
608
const allParagraphs = pos.querySelectorAll('paragraph');
609
const h1WithId = pos.querySelector('heading', { level: 1, id: 'main' });
610
611
// Navigate relationships
612
const nextHeading = pos.closest('heading')?.after?.querySelector('heading');
613
614
// Content manipulation
615
const paragraph = editor.$node('paragraph');
616
if (paragraph) {
617
paragraph.content = 'New paragraph content';
618
paragraph.setAttribute({ class: 'highlighted' });
619
}
620
621
// Range operations
622
const range = paragraph?.range;
623
if (range) {
624
editor.commands.setTextSelection(range);
625
}
626
627
// Complex navigation
628
const headings = editor.$nodes('heading');
629
headings.forEach(heading => {
630
const level = heading.attributes.level;
631
console.log(`H${level}: ${heading.textContent}`);
632
633
// Find all paragraphs under this heading
634
const paragraphs = heading.querySelectorAll('paragraph');
635
console.log(`Contains ${paragraphs.length} paragraphs`);
636
});
637
638
// Tree traversal
639
function walkNodes(nodePos: NodePos, callback: (node: NodePos) => void) {
640
callback(nodePos);
641
nodePos.children.forEach(child => walkNodes(child, callback));
642
}
643
644
walkNodes(editor.$pos(0), node => {
645
console.log(`${node.node.type.name} at ${node.pos}`);
646
});
647
```