ProseMirror is a well-documented, but fairly nuanced, "toolkit for building rich-text editors." These are Bill's notes on how all the ProseMirror pieces fit together, with embedded examples where possible.

Given that I'm still very much a ProseMirror novice as of April 2021, some of this information is surely incorrect or misleading. But I'll be constantly updating it as more experienced PM experts suggest improvements.


ProseMirror data flow:


dispatch generally: send off a transaction to EditorView to generate a new state, which will be stored (in the doc?)

NodeView can be used to control how either a node or a plugin is rendered into the html DOM

update called when PM determines that something in this node or plugins view has changed, so the view is updating itself.

"This node passed to update may be anything that the editor's update algorithm might try to draw here, so you must verify that this is a node that this node view can handle [before acting on it in the update method]."

Document Position

One widespread and kind of weird thing about position in ProseMirror is that, while they refer to indexes within the ProseMirror DOM, when you're providing the index for a node, you usually give the index one before the node's actual index. This is necessary because nodeAt and nodeAfter will only return the node that you intend to refer to if they're given an index prior to that node.

tl; dr Often hard to get without mapping from starting document state through every transaction. Three options are using forEach()nodesBetween(), or descendants().

Resolving the node before a given position: apparently $pos.doc.resolve($pos.before(1))

Resolving node after an index is the default doc.nodeAt(pos), used often

Side exercise: Examining nodeAt

Dollar sign $ it would seem always/only used to indicate a ProseMirror ResolvedPos. Examples: $start, $end, $head, $anchor, $pos. It is unclear to Bill why this convention was chosen.


Use of a transaction meta key to indicate to a plugin when it should take action (emoji popper reading meta example as triggered here, file plugin reading meta example as set here from this originating caller that wants to store info about the current local/remote UUID of the file the file-plugin is trying to display)

No examples found where a plugin tracks state of many instances

Decorations: Todo: learn how they interact


Alternate syntax, from the key codePluginKey.getState(state)


Have a type (inline, node, widget); they control aspects of how a node or view is shown without participating in the prosemirror dom


From the docs: "When the user types, or otherwise interacts with the view, it generates ‘state transactions’. Every state update has to go through updateState, and every normal editing update will happen by dispatching a transaction."

setMeta used to communicate over transactions between plugins/view/whatever else

Here's a piece of the method that updates local file UUIDs to remote file UUIDs, using setMeta to tell the history plugin not to mind the transaction it's going to generate

linkLess frequently referenced, still relevant

Data structures

linkOpen questions pending further research

Why does doc.nodesBetween() and doc.forEach return pos indexes that are one before where the corresponding node appears to be within the ProseMirror document structure? It's tempting to think it would be so that doc.resolve can be called on the position that's returned, and still select the desired node? I.e., because doc.resolve chooses the next node after the index given, perhaps these iteration functions return an index one prior to the element they're iterating to account for the behavior of doc.resolve?