Nodes vs Marks
Nodes
Nodes are structural pieces of your document. They define the content type and can contain other nodes or text.Examples: Paragraph, Heading, Image, CodeBlock, BulletList
Marks
Marks are formatting applied to text. They annotate nodes without changing the structure.Examples: Bold, Italic, Link, Code, Highlight
Key Differences
| Aspect | Nodes | Marks |
|---|---|---|
| Purpose | Define structure | Define formatting |
| Can contain | Other nodes, text, marks | Nothing (they wrap text) |
| Examples | <p>, <h1>, <img> | <strong>, <em>, <a> |
| Can overlap | No | Yes |
| Can nest | Yes | No |
Rule of thumb: If it’s a block element or defines structure, it’s a node. If it’s formatting that can be applied to text, it’s a mark.
Creating Nodes
Nodes are created usingNode.create():
/home/daytona/workspace/source/packages/extension-paragraph/src/paragraph.ts:41
Node Configuration
The unique name of the node.
The group(s) this node belongs to. Used in content expressions.Common groups:
'block'- Block-level content'inline'- Inline content'list'- List items
/home/daytona/workspace/source/packages/core/src/Node.ts:83What content this node can contain. Uses ProseMirror’s content expression syntax.Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:42Whether this is an inline node.Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:96If true, the node is treated as a single unit (cursor cannot enter it).Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:113Whether the node can be selected.Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:131Whether the node can be dragged.Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:147If true, boundaries of this node are treated as boundaries for editing operations.Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:236Rules for parsing HTML into this node.Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:256How to render this node to HTML.The
0 indicates where child content should be inserted.Source: /home/daytona/workspace/source/packages/core/src/Node.ts:284Define custom attributes for the node.Source:
/home/daytona/workspace/source/packages/core/src/Node.ts:326Creating Marks
Marks are created usingMark.create():
/home/daytona/workspace/source/packages/extension-bold/src/bold.tsx:56
Mark Configuration
The unique name of the mark.
Whether the mark should be included when typing at the edge.Source:
/home/daytona/workspace/source/packages/core/src/Mark.ts:32Which other marks this mark excludes. Use Source:
'_' to exclude all marks./home/daytona/workspace/source/packages/core/src/Mark.ts:45Whether the user can exit the mark by pressing the right arrow at the end.Source:
/home/daytona/workspace/source/packages/core/src/Mark.ts:58Whether the mark should persist when a node is split.Source:
/home/daytona/workspace/source/packages/core/src/Mark.ts:27Rules for parsing HTML into this mark.Source:
/home/daytona/workspace/source/packages/core/src/Mark.ts:102How to render this mark to HTML.Source:
/home/daytona/workspace/source/packages/core/src/Mark.ts:113Define custom attributes for the mark.Source:
/home/daytona/workspace/source/packages/core/src/Mark.ts:132Content Expressions
Content expressions define what a node can contain. They use a simple syntax:Common Patterns
| Pattern | Meaning | |
|---|---|---|
inline* | Zero or more inline elements | |
block+ | One or more block elements | |
text* | Zero or more text nodes | |
paragraph+ | One or more paragraphs | |
| `(block | paragraph)*` | Zero or more blocks or paragraphs |
Node Examples
Block Node: Heading
Inline Node: Mention
Leaf Node: Image
Mark Examples
Simple Mark: Italic
Mark with Attributes: Link
Accessing Node/Mark Types
You can access the ProseMirror types from the schema:Best Practices
Choose the Right Type
- Use nodes for structural content (paragraphs, headings, images)
- Use marks for text formatting (bold, italic, links)
- If it can overlap with other formatting, it should be a mark
- If it defines a block or structure, it should be a node
Define Content Carefully
Think carefully about what content a node should accept:
- Most block nodes use
content: 'inline*' - Container nodes use
content: 'block+' - Leaf nodes (images, etc.) have no content expression
Use Groups
Group similar nodes together:This makes it easier to reference them in content expressions.
Parse and Render Correctly
Ensure your
parseHTML and renderHTML methods handle all variations:- Different HTML tags (
<b>vs<strong>) - Inline styles
- Data attributes
Related
Extensions
Learn about the extension system
Schema
Learn how nodes and marks create the schema
Commands
Learn how to manipulate nodes and marks
Editor
Learn about the Editor class