Skip to main content
In Tiptap (and ProseMirror), your document is made up of nodes and marks. Understanding the difference is crucial for working effectively with the editor.

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

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 using Node.create():
Source: /home/daytona/workspace/source/packages/extension-paragraph/src/paragraph.ts:41

Node Configuration

string
required
The unique name of the node.
string
The group(s) this node belongs to. Used in content expressions.
Common groups:
  • 'block' - Block-level content
  • 'inline' - Inline content
  • 'list' - List items
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:83
string
What content this node can contain. Uses ProseMirror’s content expression syntax.
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:42
boolean
Whether this is an inline node.
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:96
boolean
If true, the node is treated as a single unit (cursor cannot enter it).
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:113
boolean
default:"true"
Whether the node can be selected.
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:131
boolean
default:"false"
Whether the node can be dragged.
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:147
boolean
default:"false"
If true, boundaries of this node are treated as boundaries for editing operations.
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:236
() => ParseRule[]
Rules for parsing HTML into this node.
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:256
({ node, HTMLAttributes }) => DOMOutputSpec
How 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:284
() => Attributes
Define custom attributes for the node.
Source: /home/daytona/workspace/source/packages/core/src/Node.ts:326

Creating Marks

Marks are created using Mark.create():
Source: /home/daytona/workspace/source/packages/extension-bold/src/bold.tsx:56

Mark Configuration

string
required
The unique name of the mark.
boolean | ((config) => boolean)
Whether the mark should be included when typing at the edge.
Source: /home/daytona/workspace/source/packages/core/src/Mark.ts:32
string
Which other marks this mark excludes. Use '_' to exclude all marks.
Source: /home/daytona/workspace/source/packages/core/src/Mark.ts:45
boolean
Whether the user can exit the mark by pressing the right arrow at the end.
Source: /home/daytona/workspace/source/packages/core/src/Mark.ts:58
boolean
Whether the mark should persist when a node is split.
Source: /home/daytona/workspace/source/packages/core/src/Mark.ts:27
() => ParseRule[]
Rules for parsing HTML into this mark.
Source: /home/daytona/workspace/source/packages/core/src/Mark.ts:102
({ mark, HTMLAttributes }) => DOMOutputSpec
How to render this mark to HTML.
Source: /home/daytona/workspace/source/packages/core/src/Mark.ts:113
() => Attributes
Define custom attributes for the mark.
Source: /home/daytona/workspace/source/packages/core/src/Mark.ts:132

Content Expressions

Content expressions define what a node can contain. They use a simple syntax:

Common Patterns

Node Examples

Block Node: Heading

Inline Node: Mention

Leaf Node: Image

Mark Examples

Simple Mark: Italic

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

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