prosemirror-model
The persistent document model and schema system at the core of the ProseMirror rich-text editor.
Repository Health
Technical Analysis
prosemirror-model implements the document model that every ProseMirror-based editor is built on: an immutable tree of Node and Fragment instances, together with the Schema system that defines which node and mark types a document may contain and how they nest. It underpins collaborative editors like TipTap, Atlassian’s editor, and countless custom rich-text tools that need a version-controllable, diffable representation of structured content rather than raw HTML.
Because nodes and fragments are persistent (never mutated in place), edits produce new document versions that share unchanged structure with the old one — a foundation that operational-transform and CRDT-based collaboration layers (prosemirror-collab, y-prosemirror) build directly on top of. The package also ships a bidirectional DOM bridge (DOMParser and DOMSerializer) so schemas can round-trip through contentEditable without the editor ever touching raw HTML strings directly.
What You Get
- Node and Fragment classes: an immutable tree structure representing a document’s content, size, and children
- Schema, NodeSpec, and MarkSpec APIs for declaring the node/mark types, attributes, and content-expression rules a document must obey
- ContentMatch for validating and auto-completing sequences of child nodes against a schema’s content expressions
- DOMParser and DOMSerializer for converting between the document model and browser DOM/HTML, driven by declarative parse and serialization rules
- ResolvedPos and NodeRange for resolving integer document positions into structured, depth-aware location info
- Slice and replace() for representing and applying structural edits (cut/paste-style content splicing) across the document tree
Common Use Cases
- Building a custom rich-text or structured-content editor on top of ProseMirror’s view and state packages
- Defining a constrained document schema (e.g. only headings, paragraphs, and specific inline marks) for a CMS or comment editor
- Implementing collaborative editing where documents need a diffable, structure-sharing representation for OT/CRDT syncing
- Converting between HTML and a validated document tree when importing/exporting content from a rich-text field
Under The Hood
Architecture The module is organized as a small set of tightly coupled classes rather than layered subsystems: node.ts defines Node (and its leaf TextNode variant), fragment.ts defines Fragment as the ordered child-list container each Node holds, and mark.ts defines Mark for inline formatting attached to nodes — together forming an immutable tree where every mutation-looking operation (Node.replace, Fragment.append, etc.) returns a new instance built via comparedeep.ts’s structural-sharing helpers rather than mutating in place. schema.ts defines Schema, NodeType, and MarkType, which own the NodeSpec/MarkSpec content-expression strings; those expressions are compiled by content.ts’s ContentMatch (a hand-rolled NFA→DFA compiler: parseExpr/nfa/dfa functions) into a matcher used both to validate document trees at construction time and to auto-fill required content. resolvedpos.ts’s ResolvedPos resolves a flat integer document offset into a full ancestor path, letting replace.ts’s Slice/replace() implement schema-aware content splicing (used for cut/paste-like operations) by walking up and down the resolved position’s ancestor chain to find the correct open/close boundaries. from_dom.ts (DOMParser, ~850 lines, the largest file in the module) and to_dom.ts (DOMSerializer) form the DOM boundary layer, converting between the tree and the browser DOM via declarative ParseRule/DOMOutputSpec objects rather than imperative walking code, so schema authors describe HTML shape declaratively instead of writing parser logic themselves.
Tech Stack prosemirror-model is a zero-runtime-framework TypeScript library (100% TypeScript per GitHub’s language breakdown) with exactly one runtime dependency: orderedmap (^2.0.0), used internally by Schema to keep node/mark type definitions in an order-preserving map. It has no build-time bundler config of its own — package.json’s prepare script (pm-buildhelper src/index.ts) delegates compilation and multi-format output (dist/index.js as ESM, dist/index.cjs as CJS, dist/index.d.ts for types) to @prosemirror/buildhelper, a shared tool used across the whole ProseMirror monorepo family, and the package is declared “type”: “module” with sideEffects: false so bundlers can tree-shake it. Test-only dependencies are jsdom (^20.0.0, to exercise the DOM parser/serializer without a real browser) and prosemirror-test-builder (a companion ProseMirror package providing schema/document test fixtures), run via the pm-runtests script from the same buildhelper toolchain rather than a general-purpose test runner config checked into this repo.
Code Quality Tests live under test/ as one file per module (test-node.ts, test-content.ts, test-dom.ts, test-replace.ts, test-resolve.ts, test-slice.ts, test-mark.ts, test-diff.ts) using the minimal ist assertion library and Mocha-style describe/it blocks, exercising both the public API and edge cases like custom toDebugString/leafText overrides in test-node.ts — coverage reads as thorough for a module this central, though there’s no CI config or coverage report checked into this specific clone to verify enforced thresholds. Source files are consistently documented with /// doc comments used to generate the published API reference (visible throughout node.ts, schema.ts, and content.ts), and error handling is explicit rather than silent: RangeError is thrown with descriptive messages for invalid attributes (schema.ts’s checkAttrs) and invalid content (content.ts, replace.ts’s ReplaceError), so schema/document misuse fails fast at construction time instead of producing corrupt trees. Naming is terse but consistent with the rest of the ProseMirror ecosystem (nodeSize, childCount, resolve), and TypeScript’s strict typing (readonly properties throughout Node/Fragment/Mark) enforces the immutability contract at compile time rather than relying on documentation alone.
API Design The public API favors a small number of orthogonal, composable primitives over convenience wrappers: Node, Fragment, Schema, and Slice cover essentially every editing operation, and higher-level packages (prosemirror-transform, prosemirror-state) build entirely on top of this module’s exported surface rather than reaching into internals — index.ts’s export list is deliberately narrow. Getting started requires defining a Schema with NodeSpec/MarkSpec objects before anything else, which is more upfront boilerplate than schema-less alternatives like plain contentEditable, but that investment is what makes the rest of the API (matchType, contentMatchAt, DOMParser.fromSchema) type-safe and validated. Documentation is dense and reference-oriented — the /// comments in src/*.ts are extracted into the published API docs on prosemirror.net referenced from README.md and src/README.md — favoring precision over tutorial-style hand-holding, which fits an audience of editor-framework authors more than end-application developers dropping the package in for a five-minute integration.
Used by 17 apps in this directory
Anarlog
Note Taking · AI Assistants · Productivity
Anarlog is an open-source, local-first AI meeting notetaker that records, transcribes, and summarizes meetings entirely on your device — no cloud lock-in, no mandatory account, and every note saved as a plain markdown file you own forever.
Chatwoot
Customer Support
Open-source omnichannel customer support platform with AI-powered agents, live chat, and self-hosting — a full Intercom and Zendesk alternative.
cmux
Developer Tools · AI Development
A native, Ghostty-based macOS terminal with vertical tabs, agent-aware notifications, and a scriptable browser built for running many parallel AI coding agent sessions instead of juggling tmux panes.
Discourse
Community
Open-source community platform with dynamic trust levels, real-time chat, and a powerful plugin architecture for self-hosted forums.
Dittofeed
Marketing · Automation
Open-source omni-channel customer engagement platform for automating transactional and marketing messages via email, SMS, WhatsApp, Slack, and mobile push.
Epicenter
Knowledge Management · Note Taking · Developer Tools
A local-first monorepo led by Whispering, an open-source speech-to-text app, built on an MIT toolkit that turns your data into plain Markdown and SQLite files you own instead of a database you rent.
Joplin
Note Taking
The privacy-first, open-source note-taking app with end-to-end encrypted sync, AI assistance, and a powerful plugin ecosystem across every platform.
lila (Lichess)
Community
The forever-free, ad-free, open-source chess server powering millions of real-time games, AI analysis, puzzles, and tournaments worldwide.
LimeSurvey
Forms Surveys
The world's most flexible open-source survey platform with 900+ templates, conditional logic, 80+ languages, and full GDPR compliance for any scale.