0
# Schema Definitions
1
2
Pre-built schema definitions provide common node and mark types for rich text editing. The basic schema covers fundamental text formatting, while the list schema adds structured list support.
3
4
## Capabilities
5
6
### Basic Schema
7
8
The basic schema provides essential node and mark types for general text editing.
9
10
```typescript { .api }
11
/**
12
* Complete basic schema with nodes and marks
13
*/
14
const schema: Schema;
15
```
16
17
### Basic Node Types
18
19
Core node types for document structure.
20
21
```typescript { .api }
22
/**
23
* Document root node - contains all other content
24
*/
25
const doc: NodeSpec;
26
27
/**
28
* Paragraph node - basic text container
29
*/
30
const paragraph: NodeSpec;
31
32
/**
33
* Block quote node - indented quoted content
34
*/
35
const blockquote: NodeSpec;
36
37
/**
38
* Horizontal rule node - visual separator
39
*/
40
const horizontal_rule: NodeSpec;
41
42
/**
43
* Heading node with level attribute (1-6)
44
*/
45
const heading: NodeSpec;
46
47
/**
48
* Code block node - preformatted code
49
*/
50
const code_block: NodeSpec;
51
52
/**
53
* Text node - leaf node containing actual text
54
*/
55
const text: NodeSpec;
56
57
/**
58
* Image node - embedded images with src and optional alt/title
59
*/
60
const image: NodeSpec;
61
62
/**
63
* Hard break node - forced line break
64
*/
65
const hard_break: NodeSpec;
66
```
67
68
### Basic Mark Types
69
70
Inline formatting marks for text styling.
71
72
```typescript { .api }
73
/**
74
* Link mark - hyperlinks with href attribute
75
*/
76
const link: MarkSpec;
77
78
/**
79
* Emphasis mark - italic text styling
80
*/
81
const em: MarkSpec;
82
83
/**
84
* Strong mark - bold text styling
85
*/
86
const strong: MarkSpec;
87
88
/**
89
* Code mark - inline code formatting
90
*/
91
const code: MarkSpec;
92
```
93
94
### Node and Mark Collections
95
96
Organized collections of specifications.
97
98
```typescript { .api }
99
/**
100
* All basic node specifications
101
*/
102
const nodes: {
103
doc: NodeSpec;
104
paragraph: NodeSpec;
105
blockquote: NodeSpec;
106
horizontal_rule: NodeSpec;
107
heading: NodeSpec;
108
code_block: NodeSpec;
109
text: NodeSpec;
110
image: NodeSpec;
111
hard_break: NodeSpec;
112
};
113
114
/**
115
* All basic mark specifications
116
*/
117
const marks: {
118
link: MarkSpec;
119
em: MarkSpec;
120
strong: MarkSpec;
121
code: MarkSpec;
122
};
123
```
124
125
### List Schema Extensions
126
127
The list schema provides structured list node types and commands.
128
129
```typescript { .api }
130
/**
131
* Ordered list node specification
132
*/
133
const orderedList: NodeSpec;
134
135
/**
136
* Bullet list node specification
137
*/
138
const bulletList: NodeSpec;
139
140
/**
141
* List item node specification
142
*/
143
const listItem: NodeSpec;
144
```
145
146
### List Integration
147
148
Functions to integrate list nodes into existing schemas.
149
150
```typescript { .api }
151
/**
152
* Add list nodes to an existing node collection
153
*/
154
function addListNodes(
155
nodes: { [name: string]: NodeSpec },
156
itemContent: string,
157
listGroup?: string
158
): { [name: string]: NodeSpec };
159
```
160
161
### List Commands
162
163
Commands for manipulating list structures.
164
165
```typescript { .api }
166
/**
167
* Wrap selection in a list of the given type
168
*/
169
function wrapInList(listType: NodeType, attrs?: Attrs): Command;
170
171
/**
172
* Split the current list item
173
*/
174
function splitListItem(itemType: NodeType): Command;
175
176
/**
177
* Split list item while preserving marks
178
*/
179
function splitListItemKeepMarks(itemType: NodeType): Command;
180
181
/**
182
* Lift the current list item out of its list
183
*/
184
function liftListItem(itemType: NodeType): Command;
185
186
/**
187
* Sink the current list item down into a nested list
188
*/
189
function sinkListItem(itemType: NodeType): Command;
190
```
191
192
**Usage Examples:**
193
194
```typescript
195
import { schema as basicSchema, nodes, marks } from "@tiptap/pm/schema-basic";
196
import {
197
addListNodes,
198
wrapInList,
199
splitListItem,
200
liftListItem,
201
sinkListItem
202
} from "@tiptap/pm/schema-list";
203
import { Schema } from "@tiptap/pm/model";
204
205
// Use basic schema directly
206
const simpleEditor = EditorState.create({
207
schema: basicSchema,
208
doc: basicSchema.nodeFromJSON({
209
type: "doc",
210
content: [{
211
type: "paragraph",
212
content: [{ type: "text", text: "Hello world!" }]
213
}]
214
})
215
});
216
217
// Extend basic schema with lists
218
const extendedNodes = addListNodes(nodes, "paragraph block*", "block");
219
const mySchema = new Schema({
220
nodes: extendedNodes,
221
marks: marks
222
});
223
224
// Create list commands
225
const toggleOrderedList = wrapInList(mySchema.nodes.orderedList);
226
const toggleBulletList = wrapInList(mySchema.nodes.bulletList);
227
const splitItem = splitListItem(mySchema.nodes.listItem);
228
const liftItem = liftListItem(mySchema.nodes.listItem);
229
const sinkItem = sinkListItem(mySchema.nodes.listItem);
230
231
// Use in keymap
232
const listKeymap = keymap({
233
"Mod-Shift-7": toggleOrderedList,
234
"Mod-Shift-8": toggleBulletList,
235
"Enter": splitItem,
236
"Mod-[": liftItem,
237
"Mod-]": sinkItem,
238
"Tab": sinkItem,
239
"Shift-Tab": liftItem
240
});
241
242
// Custom schema with additional nodes
243
const customSchema = new Schema({
244
nodes: {
245
...nodes,
246
// Add custom nodes
247
callout: {
248
content: "block+",
249
group: "block",
250
defining: true,
251
attrs: { type: { default: "info" } },
252
parseDOM: [{
253
tag: "div.callout",
254
getAttrs: (dom) => ({ type: dom.getAttribute("data-type") })
255
}],
256
toDOM: (node) => ["div", {
257
class: "callout",
258
"data-type": node.attrs.type
259
}, 0]
260
},
261
262
// Add list nodes
263
...addListNodes(nodes, "paragraph block*", "block")
264
},
265
266
marks: {
267
...marks,
268
// Add custom marks
269
highlight: {
270
attrs: { color: { default: "yellow" } },
271
parseDOM: [{
272
tag: "mark",
273
getAttrs: (dom) => ({ color: dom.style.backgroundColor })
274
}],
275
toDOM: (mark) => ["mark", {
276
style: `background-color: ${mark.attrs.color}`
277
}, 0]
278
}
279
}
280
});
281
```
282
283
## Advanced Schema Customization
284
285
### Custom Node Definitions
286
287
Extend or modify existing node types for specific needs.
288
289
```typescript
290
// Custom heading with ID support
291
const headingWithId = {
292
...heading,
293
attrs: {
294
level: { default: 1 },
295
id: { default: null }
296
},
297
parseDOM: [
298
...heading.parseDOM.map(rule => ({
299
...rule,
300
getAttrs: (dom) => ({
301
level: +dom.tagName.slice(1),
302
id: dom.id || null
303
})
304
}))
305
],
306
toDOM: (node) => {
307
const attrs = { id: node.attrs.id };
308
return [`h${node.attrs.level}`, attrs, 0];
309
}
310
};
311
312
// Code block with language support
313
const codeBlockWithLang = {
314
...code_block,
315
attrs: {
316
language: { default: null },
317
showLineNumbers: { default: false }
318
},
319
parseDOM: [{
320
tag: "pre",
321
preserveWhitespace: "full",
322
getAttrs: (dom) => ({
323
language: dom.getAttribute("data-language"),
324
showLineNumbers: dom.hasAttribute("data-line-numbers")
325
})
326
}],
327
toDOM: (node) => {
328
const attrs = {};
329
if (node.attrs.language) {
330
attrs["data-language"] = node.attrs.language;
331
}
332
if (node.attrs.showLineNumbers) {
333
attrs["data-line-numbers"] = "";
334
}
335
return ["pre", attrs, ["code", 0]];
336
}
337
};
338
```
339
340
### List Customization
341
342
Customize list behavior and styling.
343
344
```typescript
345
// Custom list with additional attributes
346
const customOrderedList = {
347
...orderedList,
348
attrs: {
349
order: { default: 1 },
350
style: { default: "decimal" }
351
},
352
parseDOM: [{
353
tag: "ol",
354
getAttrs: (dom) => ({
355
order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1,
356
style: dom.style.listStyleType || "decimal"
357
})
358
}],
359
toDOM: (node) => {
360
const attrs = {};
361
if (node.attrs.order !== 1) {
362
attrs.start = node.attrs.order;
363
}
364
if (node.attrs.style !== "decimal") {
365
attrs.style = `list-style-type: ${node.attrs.style}`;
366
}
367
return ["ol", attrs, 0];
368
}
369
};
370
371
// Task list item extension
372
const taskListItem = {
373
content: "paragraph block*",
374
defining: true,
375
attrs: {
376
checked: { default: false },
377
id: { default: null }
378
},
379
parseDOM: [{
380
tag: "li[data-task-item]",
381
getAttrs: (dom) => ({
382
checked: dom.hasAttribute("data-checked"),
383
id: dom.getAttribute("data-id")
384
})
385
}],
386
toDOM: (node) => {
387
const attrs = { "data-task-item": "" };
388
if (node.attrs.checked) {
389
attrs["data-checked"] = "";
390
}
391
if (node.attrs.id) {
392
attrs["data-id"] = node.attrs.id;
393
}
394
return ["li", attrs, 0];
395
}
396
};
397
```
398
399
### Schema Validation
400
401
Ensure schema consistency and proper configuration.
402
403
```typescript
404
// Validate schema configuration
405
function validateCustomSchema(schemaSpec: SchemaSpec): string[] {
406
const errors: string[] = [];
407
408
// Check required nodes
409
const requiredNodes = ["doc", "paragraph", "text"];
410
for (const node of requiredNodes) {
411
if (!schemaSpec.nodes[node]) {
412
errors.push(`Missing required node: ${node}`);
413
}
414
}
415
416
// Validate content expressions
417
for (const [name, spec] of Object.entries(schemaSpec.nodes)) {
418
if (spec.content && !isValidContentExpression(spec.content)) {
419
errors.push(`Invalid content expression for ${name}: ${spec.content}`);
420
}
421
}
422
423
return errors;
424
}
425
426
// Create schema with validation
427
function createValidatedSchema(schemaSpec: SchemaSpec): Schema {
428
const errors = validateCustomSchema(schemaSpec);
429
if (errors.length > 0) {
430
throw new Error(`Schema validation failed:\n${errors.join("\n")}`);
431
}
432
return new Schema(schemaSpec);
433
}
434
```
435
436
## Types
437
438
```typescript { .api }
439
/**
440
* Schema specification for nodes and marks
441
*/
442
interface SchemaSpec {
443
nodes: { [name: string]: NodeSpec };
444
marks?: { [name: string]: MarkSpec };
445
topNode?: string;
446
}
447
448
/**
449
* List integration options
450
*/
451
interface ListOptions {
452
itemContent?: string;
453
listGroup?: string;
454
}
455
456
/**
457
* Custom node attributes
458
*/
459
interface CustomNodeAttrs {
460
[key: string]: AttributeSpec;
461
}
462
```