Comprehensive wrapper around ProseMirror packages providing unified entry point for rich text editing functionality in Tiptap framework
—
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.
The basic schema provides essential node and mark types for general text editing.
/**
* Complete basic schema with nodes and marks
*/
const schema: Schema;Core node types for document structure.
/**
* Document root node - contains all other content
*/
const doc: NodeSpec;
/**
* Paragraph node - basic text container
*/
const paragraph: NodeSpec;
/**
* Block quote node - indented quoted content
*/
const blockquote: NodeSpec;
/**
* Horizontal rule node - visual separator
*/
const horizontal_rule: NodeSpec;
/**
* Heading node with level attribute (1-6)
*/
const heading: NodeSpec;
/**
* Code block node - preformatted code
*/
const code_block: NodeSpec;
/**
* Text node - leaf node containing actual text
*/
const text: NodeSpec;
/**
* Image node - embedded images with src and optional alt/title
*/
const image: NodeSpec;
/**
* Hard break node - forced line break
*/
const hard_break: NodeSpec;Inline formatting marks for text styling.
/**
* Link mark - hyperlinks with href attribute
*/
const link: MarkSpec;
/**
* Emphasis mark - italic text styling
*/
const em: MarkSpec;
/**
* Strong mark - bold text styling
*/
const strong: MarkSpec;
/**
* Code mark - inline code formatting
*/
const code: MarkSpec;Organized collections of specifications.
/**
* All basic node specifications
*/
const nodes: {
doc: NodeSpec;
paragraph: NodeSpec;
blockquote: NodeSpec;
horizontal_rule: NodeSpec;
heading: NodeSpec;
code_block: NodeSpec;
text: NodeSpec;
image: NodeSpec;
hard_break: NodeSpec;
};
/**
* All basic mark specifications
*/
const marks: {
link: MarkSpec;
em: MarkSpec;
strong: MarkSpec;
code: MarkSpec;
};The list schema provides structured list node types and commands.
/**
* Ordered list node specification
*/
const orderedList: NodeSpec;
/**
* Bullet list node specification
*/
const bulletList: NodeSpec;
/**
* List item node specification
*/
const listItem: NodeSpec;Functions to integrate list nodes into existing schemas.
/**
* Add list nodes to an existing node collection
*/
function addListNodes(
nodes: { [name: string]: NodeSpec },
itemContent: string,
listGroup?: string
): { [name: string]: NodeSpec };Commands for manipulating list structures.
/**
* Wrap selection in a list of the given type
*/
function wrapInList(listType: NodeType, attrs?: Attrs): Command;
/**
* Split the current list item
*/
function splitListItem(itemType: NodeType): Command;
/**
* Split list item while preserving marks
*/
function splitListItemKeepMarks(itemType: NodeType): Command;
/**
* Lift the current list item out of its list
*/
function liftListItem(itemType: NodeType): Command;
/**
* Sink the current list item down into a nested list
*/
function sinkListItem(itemType: NodeType): Command;Usage Examples:
import { schema as basicSchema, nodes, marks } from "@tiptap/pm/schema-basic";
import {
addListNodes,
wrapInList,
splitListItem,
liftListItem,
sinkListItem
} from "@tiptap/pm/schema-list";
import { Schema } from "@tiptap/pm/model";
// Use basic schema directly
const simpleEditor = EditorState.create({
schema: basicSchema,
doc: basicSchema.nodeFromJSON({
type: "doc",
content: [{
type: "paragraph",
content: [{ type: "text", text: "Hello world!" }]
}]
})
});
// Extend basic schema with lists
const extendedNodes = addListNodes(nodes, "paragraph block*", "block");
const mySchema = new Schema({
nodes: extendedNodes,
marks: marks
});
// Create list commands
const toggleOrderedList = wrapInList(mySchema.nodes.orderedList);
const toggleBulletList = wrapInList(mySchema.nodes.bulletList);
const splitItem = splitListItem(mySchema.nodes.listItem);
const liftItem = liftListItem(mySchema.nodes.listItem);
const sinkItem = sinkListItem(mySchema.nodes.listItem);
// Use in keymap
const listKeymap = keymap({
"Mod-Shift-7": toggleOrderedList,
"Mod-Shift-8": toggleBulletList,
"Enter": splitItem,
"Mod-[": liftItem,
"Mod-]": sinkItem,
"Tab": sinkItem,
"Shift-Tab": liftItem
});
// Custom schema with additional nodes
const customSchema = new Schema({
nodes: {
...nodes,
// Add custom nodes
callout: {
content: "block+",
group: "block",
defining: true,
attrs: { type: { default: "info" } },
parseDOM: [{
tag: "div.callout",
getAttrs: (dom) => ({ type: dom.getAttribute("data-type") })
}],
toDOM: (node) => ["div", {
class: "callout",
"data-type": node.attrs.type
}, 0]
},
// Add list nodes
...addListNodes(nodes, "paragraph block*", "block")
},
marks: {
...marks,
// Add custom marks
highlight: {
attrs: { color: { default: "yellow" } },
parseDOM: [{
tag: "mark",
getAttrs: (dom) => ({ color: dom.style.backgroundColor })
}],
toDOM: (mark) => ["mark", {
style: `background-color: ${mark.attrs.color}`
}, 0]
}
}
});Extend or modify existing node types for specific needs.
// Custom heading with ID support
const headingWithId = {
...heading,
attrs: {
level: { default: 1 },
id: { default: null }
},
parseDOM: [
...heading.parseDOM.map(rule => ({
...rule,
getAttrs: (dom) => ({
level: +dom.tagName.slice(1),
id: dom.id || null
})
}))
],
toDOM: (node) => {
const attrs = { id: node.attrs.id };
return [`h${node.attrs.level}`, attrs, 0];
}
};
// Code block with language support
const codeBlockWithLang = {
...code_block,
attrs: {
language: { default: null },
showLineNumbers: { default: false }
},
parseDOM: [{
tag: "pre",
preserveWhitespace: "full",
getAttrs: (dom) => ({
language: dom.getAttribute("data-language"),
showLineNumbers: dom.hasAttribute("data-line-numbers")
})
}],
toDOM: (node) => {
const attrs = {};
if (node.attrs.language) {
attrs["data-language"] = node.attrs.language;
}
if (node.attrs.showLineNumbers) {
attrs["data-line-numbers"] = "";
}
return ["pre", attrs, ["code", 0]];
}
};Customize list behavior and styling.
// Custom list with additional attributes
const customOrderedList = {
...orderedList,
attrs: {
order: { default: 1 },
style: { default: "decimal" }
},
parseDOM: [{
tag: "ol",
getAttrs: (dom) => ({
order: dom.hasAttribute("start") ? +dom.getAttribute("start") : 1,
style: dom.style.listStyleType || "decimal"
})
}],
toDOM: (node) => {
const attrs = {};
if (node.attrs.order !== 1) {
attrs.start = node.attrs.order;
}
if (node.attrs.style !== "decimal") {
attrs.style = `list-style-type: ${node.attrs.style}`;
}
return ["ol", attrs, 0];
}
};
// Task list item extension
const taskListItem = {
content: "paragraph block*",
defining: true,
attrs: {
checked: { default: false },
id: { default: null }
},
parseDOM: [{
tag: "li[data-task-item]",
getAttrs: (dom) => ({
checked: dom.hasAttribute("data-checked"),
id: dom.getAttribute("data-id")
})
}],
toDOM: (node) => {
const attrs = { "data-task-item": "" };
if (node.attrs.checked) {
attrs["data-checked"] = "";
}
if (node.attrs.id) {
attrs["data-id"] = node.attrs.id;
}
return ["li", attrs, 0];
}
};Ensure schema consistency and proper configuration.
// Validate schema configuration
function validateCustomSchema(schemaSpec: SchemaSpec): string[] {
const errors: string[] = [];
// Check required nodes
const requiredNodes = ["doc", "paragraph", "text"];
for (const node of requiredNodes) {
if (!schemaSpec.nodes[node]) {
errors.push(`Missing required node: ${node}`);
}
}
// Validate content expressions
for (const [name, spec] of Object.entries(schemaSpec.nodes)) {
if (spec.content && !isValidContentExpression(spec.content)) {
errors.push(`Invalid content expression for ${name}: ${spec.content}`);
}
}
return errors;
}
// Create schema with validation
function createValidatedSchema(schemaSpec: SchemaSpec): Schema {
const errors = validateCustomSchema(schemaSpec);
if (errors.length > 0) {
throw new Error(`Schema validation failed:\n${errors.join("\n")}`);
}
return new Schema(schemaSpec);
}/**
* Schema specification for nodes and marks
*/
interface SchemaSpec {
nodes: { [name: string]: NodeSpec };
marks?: { [name: string]: MarkSpec };
topNode?: string;
}
/**
* List integration options
*/
interface ListOptions {
itemContent?: string;
listGroup?: string;
}
/**
* Custom node attributes
*/
interface CustomNodeAttrs {
[key: string]: AttributeSpec;
}Install with Tessl CLI
npx tessl i tessl/npm-tiptap--pm