NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #2747 most downloaded on npm
bubble-menu extension for tiptap
Last release 4 days ago
30 Sep 2026
Ships fairly regularly
a new release about every 2 weeks
Nearly every release is documented
notes for 58 of the last 60 stable releases
1 version withdrawn
withdrawn after publishing
6 years old
370 releases · first in 2021
One column per quarter.
The mathematics extension no longer crashes the editor in older WebKit browsers and WKWebView.
TypeError thrown when the source editor is destroyed right after dragging content into another editor.splitBlock no longer throws TransformError: Inserted content deeper than insertion position when the selection spans block boundaries (for example from the start of one paragraph into another block, or across an isolating node). The command now returns false when the split is not possible.:::name {…} :::) indented by up to 3 spaces are now tokenized, matching CommonMark indentation rules for block constructs.', ') are now decoded when parsing markdown, instead of showing up as literal text in the editor.role="textbox" after setOptions() when editorProps.attributes is set. Before, a re-render in React replaced the default role with the user attributes. Attributes passed as a function now get the default role as well.parseMarkdown and renderMarkdown to the configured extension so hooks can read this.options and this.name without an Editor.keepOnSplit: false now reset on the new item, not on the one that keeps the text.contentDOM no longer ignore selection mutations, so ProseMirror moves the caret back to a valid position when the browser places it inside the node view.parseMarkdown and renderMarkdown to the configured extension so hooks can read this.options and this.name without an Editor.colspan="1" and rowspan="1" attributes. Cells that actually span still render them, matching how prosemirror-tables serializes spans.<audio> and <video> with a closing tag instead of self-closing them, so browsers no longer nest the following content inside the first audio element.renderHTML returns several nested child elements no longer adds stray commas between them in the HTML string output.@tiptap/extension-collaboration-caret
skipLibCheck is turned off.Bump prosemirror-view to ^1.42.3 , which fixes an XSS vulnerability where pasting crafted HTML could run arbitrary JavaScript ( GHSA-c8x8-7fp4-3x9w ).
prosemirror-view to ^1.42.3, which fixes an XSS vulnerability where pasting crafted HTML could run arbitrary JavaScript (GHSA-c8x8-7fp4-3x9w).Fix freezes in framework-based node views on iOS and Android
e7bf804 : Align selected with ProseMirror node selections by default, expose text selections through selectionInside , and keep selectedOnTextSelectio
selected with ProseMirror node selections by default, expose text selections through selectionInside, and keep selectedOnTextSelection compatible.Nested lists exported to Markdown now keep their hierarchy when the file is read back by other Markdown tools.
src attribute no longer crashes the editor. The embed is kept without a source.Fix a denial-of-service risk where crafted block or inline Markdown attributes could consume excessive CPU and block the browser or server event loop.
Prevent untrusted HTML attributes from changing an object's prototype when merged with mergeAttributes .
mergeAttributes.Unsetting one text style inside a blockquote no longer removes the other text styles in it.
/live/<id>) now embed the video.AiInsertReveal extension (@tiptap/ai-toolkit/streaming-reveal) to fade in text as the AI streams it into a collaborative document./live/<id>) now embed the video.AiInsertReveal extension (@tiptap/ai-toolkit/streaming-reveal) to fade in text as the AI streams it into a collaborative document.Keep mixed JSX children as separate siblings in DOM output.
editor.chain and editor.can can not be accessed on editor initializationeditor.chain and editor.can can not be accessed on editor initializationFixed table markdown pipe escaping so the extension loads on older Safari and iOS versions (WebKit before Safari 16.4).
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Ti
ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
ceb0dac: ListKeymap now registers a Tab shortcut that sinks a top-level textblock into the previous list's last item. Pressing Tab at the start of a paragraph right after a bullet/ordered/task list moves the paragraph inside the last list item. The handler does nothing when the cursor is already inside a list item (sinkListItem keeps working), when there is no list before the paragraph, when the caret is mid-textblock, or when the selection is not a text selection (for example a gap cursor).
@tiptap/core also exposes a new getPreviousBlockSibling($pos) helper that returns the block-level sibling before the cursor's textblock, or null at the first child of the block parent.
ceb0dac: ListKeymap now registers a Tab shortcut that sinks a top-level textblock into the previous list's last item. Pressing Tab at the start of a paragraph right after a bullet/ordered/task list moves the paragraph inside the last list item. The handler does nothing when the cursor is already inside a list item (sinkListItem keeps working), when there is no list before the paragraph, when the caret is mid-textblock, or when the selection is not a text selection (for example a gap cursor).
@tiptap/core also exposes a new getPreviousBlockSibling($pos) helper that returns the block-level sibling before the cursor's textblock, or null at the first child of the block parent.
ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
insertContent, insertContentAt and setContent failing when prosemirror-model is loaded more than once.@tiptap/* dependencies to the exact version it was released with, so installing a specific StarterKit version gives you that version's extension set instead of the newest one.ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
<b> tag no longer parses into an invalid document. The tag is dropped and its text is kept../schema-list export map pointing types at dist/schema/, which is not emitted. Tools that read the types condition directly could not resolve @tiptap/pm/schema-list.DragHandle breaking drag-and-drop when onNodeChange is an inline callback, by no longer re-registering its plugin when a callback's identity changes.ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}
There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
ceb0dac: ListKeymap now registers a Tab shortcut that sinks a top-level textblock into the previous list's last item. Pressing Tab at the start of a paragraph right after a bullet/ordered/task list moves the paragraph inside the last list item. The handler does nothing when the cursor is already inside a list item (sinkListItem keeps working), when there is no list before the paragraph, when the caret is mid-textblock, or when the selection is not a text selection (for example a gap cursor).
@tiptap/core also exposes a new getPreviousBlockSibling($pos) helper that returns the block-level sibling before the cursor's textblock, or null at the first child of the block parent.
ceb0dac: ListKeymap now registers a Tab shortcut that sinks a top-level textblock into the previous list's last item. Pressing Tab at the start of a paragraph right after a bullet/ordered/task list moves the paragraph inside the last list item. The handler does nothing when the cursor is already inside a list item (sinkListItem keeps working), when there is no list before the paragraph, when the caret is mid-textblock, or when the selection is not a text selection (for example a gap cursor).
@tiptap/core also exposes a new getPreviousBlockSibling($pos) helper that returns the block-level sibling before the cursor's textblock, or null at the first child of the block parent.
ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}
There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
insertContent, insertContentAt and setContent failing when prosemirror-model is loaded more than once.@tiptap/* dependencies to the exact version it was released with, so installing a specific StarterKit version gives you that version's extension set instead of the newest one.ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}
There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
<b> tag no longer parses into an invalid document. The tag is dropped and its text is kept../schema-list export map pointing types at dist/schema/, which is not emitted. Tools that read the types condition directly could not resolve @tiptap/pm/schema-list.DragHandle breaking drag-and-drop when onNodeChange is an inline callback, by no longer re-registering its plugin when a callback's identity changes.ceb0dac: New Decorations API
Finally the decorations API is here! Even though Decorations itself are nothing new in ProseMirror, the new API makes it much easier to use them in Tiptap without leaving your extensions.
Decorations change how the document looks without changing the document itself. Highlighting search results, marking spelling mistakes, showing collaborator cursors, putting a drag handle next to every block.
Until now you had to write a ProseMirror plugin by hand for this, keep the decoration set in plugin state, and map it forward on every transaction. Extensions can now declare decorations directly with a new addDecorations() hook.
addDecorations() {
return {
create: ({ state }) =>
// findMatches can be any function that returns an array of { from, to } ranges
findMatches(state.doc).map(match =>
Decoration.Inline(match.from, match.to, { class: 'highlight' }),
),
}
}
There are three kinds. Decoration.Inline() styles a range of text. Decoration.Node() puts attributes on a block's DOM element. Decoration.Widget() renders your own element at a single position.
Every extension that declares decorations is collected into one plugin, so several extensions can decorate the same document without fighting over it.
Doing less work on every keystroke
By default decorations are rebuilt whenever the document changes. That is fine for small documents and wasteful for large ones, so there are two ways to narrow it down.
shouldUpdate() skips transactions you do not care about. If your decorations only depend on headings, ignore everything else.
update: 'changedRanges' together with createInRange() only rescans the blocks that actually changed. On a long document this is the difference between scanning the whole thing on every keystroke and scanning one paragraph.
For decorations driven by data outside the editor, like comments loaded from a server, use update: 'manual' and refresh them yourself with editor.commands.updateDecorations().
React and Vue components as widgets
ReactWidgetRenderer and VueWidgetRenderer render a real component into a widget decoration, inside your existing app context. Providers, context and stores work as usual.
Widgets take a key. Reuse the same key and the component instance stays mounted while the document changes around it, so local state such as an open menu, a counter or a half-typed input survives editing. Use a stable id from your own data, not a position or a list index, otherwise the component remounts and loses that state.
Widgets also accept the ProseMirror options side, relaxedSide, stopEvent and ignoreSelection.
Documentation
Fixed the caret jumping back to the previous block when pressing Enter inside a React node view.
6d901e7 : Fix caret placement after splitting a block rendered with a React NodeView.
Add official RubyText extension for HTML ruby text annotations with non-editable annotations and mark-based document storage. The click-to-edit annota
renderAnnotationEditor option.isAndroid() where comparing navigator.platform against the literal 'Android' with === could fail to compile under some lib.dom.d.ts typings ("types have no overlap"). Switched to the same .includes() pattern already used by isiOS(), which is not affected by this TypeScript narrowing issue. No runtime behavior change.AllSelection (for example right after Ctrl/Cmd+A) left a lingering "phantom" selection highlight over the emptied document instead of a text cursor. deleteSelection now collapses the selection to a cursor.getPos() now returns undefined instead of throwing when the position cannot be resolved yet, for example when React 19 renders a node view component while the editor view is still updating.onContentError throwing when calling editor.commands from inside the handler on initial load with invalid content. The editor now has a usable state (seeded from the stripped fallback document) before onContentError fires.editor.$pos() returning the wrong node inside container nodes, for example the list item instead of the list.insertDefaultBlock to insert the default textblock allowed at a position. It accepts an optional position, attributes, content, and selection-update option.@tiptap/extension-find-and-replace extension. It searches the document for a term, highlights all matches with decorations, and replaces the current or all matches. Supports case-sensitive, whole-word, and RE2-compatible regex search, plus commands to jump between results. Regex search avoids catastrophic backtracking but does not support lookarounds or backreferences.prosemirror-model to ^1.25.11, fixing pasting content copied from the editor inserting extra empty paragraphs (a regression introduced in prosemirror-view 1.42.0).ArrowUp in a code block that is the first node in the document did nothing, leaving no way to insert content above it. A new default block is now inserted above the code block, mirroring the existing ArrowDown behavior. The behavior can be disabled via the new exitOnArrowUp option.insertContent/insertContentAt) throwing RangeError: Invalid content for node tableCell/tableHeader: <>. Empty <td>/<th> elements are now backfilled with the cell's default block content, matching the behavior you already get from setContent.<br> instead of being collapsed into a space, so they survive a parse/serialize round trip.useEditorState not re-rendering components when editor.setEditable() changes the editor's editable state, since that call only emits an update event and never a transaction.<node-view-content as="tbody"> (and similar restricted-content elements) rendering with an extra wrapper <div> nested inside them, which broke tables in Vue node views. Note: keep <node-view-content> mounted (use v-show, not v-if) — conditionally remounting it can leave ProseMirror attached to the old element.[Tiptap](https://tiptap.dev) or [Tiptap](https://tiptap.dev "Rich text editor") can now automatically be converted into a link. The behavior is opt-in, enable it with the new markdownLinks option.renderAnnotationEditor option.isAndroid() where comparing navigator.platform against the literal 'Android' with === could fail to compile under some lib.dom.d.ts typings ("types have no overlap"). Switched to the same .includes() pattern already used by isiOS(), which is not affected by this TypeScript narrowing issue. No runtime behavior change.AllSelection (for example right after Ctrl/Cmd+A) left a lingering "phantom" selection highlight over the emptied document instead of a text cursor. deleteSelection now collapses the selection to a cursor.getPos() now returns undefined instead of throwing when the position cannot be resolved yet, for example when React 19 renders a node view component while the editor view is still updating.onContentError throwing when calling editor.commands from inside the handler on initial load with invalid content. The editor now has a usable state (seeded from the stripped fallback document) before onContentError fires.editor.$pos() returning the wrong node inside container nodes, for example the list item instead of the list.insertDefaultBlock to insert the default textblock allowed at a position. It accepts an optional position, attributes, content, and selection-update option.@tiptap/extension-find-and-replace extension. It searches the document for a term, highlights all matches with decorations, and replaces the current or all matches. Supports case-sensitive, whole-word, and RE2-compatible regex search, plus commands to jump between results. Regex search avoids catastrophic backtracking but does not support lookarounds or backreferences.prosemirror-model to ^1.25.11, fixing pasting content copied from the editor inserting extra empty paragraphs (a regression introduced in prosemirror-view 1.42.0).ArrowUp in a code block that is the first node in the document did nothing, leaving no way to insert content above it. A new default block is now inserted above the code block, mirroring the existing ArrowDown behavior. The behavior can be disabled via the new exitOnArrowUp option.insertContent/insertContentAt) throwing RangeError: Invalid content for node tableCell/tableHeader: <>. Empty <td>/<th> elements are now backfilled with the cell's default block content, matching the behavior you already get from setContent.<br> instead of being collapsed into a space, so they survive a parse/serialize round trip.useEditorState not re-rendering components when editor.setEditable() changes the editor's editable state, since that call only emits an update event and never a transaction.<node-view-content as="tbody"> (and similar restricted-content elements) rendering with an extra wrapper <div> nested inside them, which broke tables in Vue node views. Note: keep <node-view-content> mounted (use v-show, not v-if) — conditionally remounting it can leave ProseMirror attached to the old element.[Tiptap](https://tiptap.dev) or [Tiptap](https://tiptap.dev "Rich text editor") can now automatically be converted into a link. The behavior is opt-in, enable it with the new markdownLinks option.8614730 : Fix the cursor moving to the details summary after typing in content at the start of a document.
(216) 555-1234 as an ordered list. A number followed by ) mid-line is no longer treated as a list marker.width and height values like '100%' or '20rem' in the YouTube extension. Non-numeric values in parsed HTML are now preserved instead of being converted to numbers.(216) 555-1234 as an ordered list. A number followed by ) mid-line is no longer treated as a list marker.width and height values like '100%' or '20rem' in the YouTube extension. Non-numeric values in parsed HTML are now preserved instead of being converted to numbers.0fde1e8 : Fixed the Selection extension leaving the native browser selection visible on blur, where it overlapped the selection decoration. The native
Selection extension leaving the native browser selection visible on blur, where it overlapped the selection decoration. The native selection is now cleared on blur and restored on focus.TypeError: Cannot read properties of undefined (reading 'type'). It now bails out so backspace is a no-op at the document start.@tiptap/pm as a peer dependency so bundlers resolve ProseMirror packages from the app instead of duplicating prosemirror-model inside @tiptap/extension-blockquote.<col width> in a table's <colgroup> being ignored when parsing HTML. The width of the first column was always dropped because the cell index 0 failed a truthiness check, and header cells (<th>) never read the colgroup at all. Both table cells and table headers now fall back to the matching <col> element's width attribute when they have no colwidth attribute of their own.|) GFM tables. Cells containing expressions like `||` or `a || b` now parse correctly instead of splitting into extra columns and losing their code formatting.- item) nested under a task-list parent (- [ ]) was silently dropped from the parsed document. The task-list tokenizer's nested parser stopped at the first non-checkbox line and discarded everything after it; that remainder is now lexed and kept as sibling blocks (a bullet list or paragraph inside the parent task item), matching how mixed lists already behave at the top level.use client directive so @tiptap/react can be imported from React Server Components without crashing. Core symbols re-exported through @tiptap/react now cross the client boundary too, so import them from @tiptap/core directly in server code.94de762 : Fix deleteSelection to delete content across all selection ranges instead of only the first range. This restores multi-cell table selections
deleteSelection to delete content across all selection ranges instead of only the first range. This restores multi-cell table selections and other custom selections with multiple ranges.94de762: Fix markdown parsing bugs where block elements right after an ordered list item (with no blank line in between) were wrongly treated as lazy continuation of the list item, instead of terminating the list the way other markdown parsers do:
---, ***, ___, * * *) were swallowed into the list item as literal paragraph text — along with every line after them. They now terminate the list and become a horizontal rule.``` and ~~~) were nested inside the list item. They now terminate the list and become a top-level code block.- item) were nested inside the ordered list item. They now terminate the ordered list and start a new top-level bullet list. Indented bullets still nest inside the item as before.An indented ***/___ inside item content is now also parsed as a horizontal rule inside the item instead of literal text. A --- line directly below item paragraph text keeps its current behavior because it is a setext heading underline per CommonMark, not a thematic break.
94de762: Fix indented ordered list items (e.g. one leading space before the marker, as happens when a top-level ordered list is itself nested inside another list) losing inline formatting during markdown parsing. The custom ordered-list markdown tokenizer built its nested structure with a hardcoded base indentation of 0, so an item whose actual indentation was non-zero never matched, causing the tokenizer to silently produce zero items and bail out — falling back to a path that left the item's content as literal, unparsed text instead of running it through inline tokenization (bold, italic, etc. were lost). The base indentation is now taken from the first collected item instead of being hardcoded.
Updated dependencies [94de762]
StateField<DecorationSet> that only re-computes decorations for top-level nodes touched by each transaction. This eliminates the dependency on DOM measurement (posAtCoords), requestAnimationFrame scheduling, and scroll listeners that caused flickering under collaboration, occlusion, and rapid edits.deleteSelection to delete content across all selection ranges instead of only the first range. This restores multi-cell table selections and other custom selections with multiple ranges.94de762: Fix markdown parsing bugs where block elements right after an ordered list item (with no blank line in between) were wrongly treated as lazy continuation of the list item, instead of terminating the list the way other markdown parsers do:
---, ***, ___, * * *) were swallowed into the list item as literal paragraph text — along with every line after them. They now terminate the list and become a horizontal rule.``` and ~~~) were nested inside the list item. They now terminate the list and become a top-level code block.- item) were nested inside the ordered list item. They now terminate the ordered list and start a new top-level bullet list. Indented bullets still nest inside the item as before.An indented ***/___ inside item content is now also parsed as a horizontal rule inside the item instead of literal text. A --- line directly below item paragraph text keeps its current behavior because it is a setext heading underline per CommonMark, not a thematic break.
94de762: Fix indented ordered list items (e.g. one leading space before the marker, as happens when a top-level ordered list is itself nested inside another list) losing inline formatting during markdown parsing. The custom ordered-list markdown tokenizer built its nested structure with a hardcoded base indentation of 0, so an item whose actual indentation was non-zero never matched, causing the tokenizer to silently produce zero items and bail out — falling back to a path that left the item's content as literal, unparsed text instead of running it through inline tokenization (bold, italic, etc. were lost). The base indentation is now taken from the first collected item instead of being hardcoded.
Updated dependencies [94de762]
StateField<DecorationSet> that only re-computes decorations for top-level nodes touched by each transaction. This eliminates the dependency on DOM measurement (posAtCoords), requestAnimationFrame scheduling, and scroll listeners that caused flickering under collaboration, occlusion, and rapid edits.ceebb31 : Fix arrow key navigation past emoji nodes in Firefox. Previously, pressing ArrowLeft with the cursor adjacent to an inline non-selectable em
consumePasteEvent option to the file-handler extension. When true, handlePaste returns true even when HTML content is present in the clipboard, preventing paste rules from other extensions from creating duplicate content. Default is false.isChangeOrigin document rebuilds, and selection restore runs via appendTransaction after the drop transaction settles.undefined HTMLAttributes (target, rel, class) to null so ProseMirror does not emit "No value supplied for attribute" warnings when these options are explicitly set to undefined.### inside the list instead of an actual heading. Headings now end the list and get parsed properly, the way other markdown parsers handle it. Indented headings inside a list item are also parsed as real headings now instead of literal text.editor or the instance fields are requiredclassName prop now updates the element in place without re-registering the drag handle plugin.unhandledNode and unhandledMark for node or mark types missing from the schema; such content now falls back to those renderers instead of throwing in Node.fromJSONconsumePasteEvent option to the file-handler extension. When true, handlePaste returns true even when HTML content is present in the clipboard, preventing paste rules from other extensions from creating duplicate content. Default is false.isChangeOrigin document rebuilds, and selection restore runs via appendTransaction after the drop transaction settles.undefined HTMLAttributes (target, rel, class) to null so ProseMirror does not emit "No value supplied for attribute" warnings when these options are explicitly set to undefined.### inside the list instead of an actual heading. Headings now end the list and get parsed properly, the way other markdown parsers handle it. Indented headings inside a list item are also parsed as real headings now instead of literal text.editor or the instance fields are requiredclassName prop now updates the element in place without re-registering the drag handle plugin.unhandledNode and unhandledMark for node or mark types missing from the schema; such content now falls back to those renderers instead of throwing in Node.fromJSONa16901d: Respect margin properties passed via dragImageProperties on the drag preview. The clone reset its margin to 0 after copying styles, which dis
dragImageProperties on the drag preview. The clone reset its margin to 0 after copying styles, which discarded any margin the user explicitly requested. The reset now runs only when no margin property is listed in dragImageProperties, so the drag image can keep the same spacing as the live block.data-placeholder attribute on empty blocks. The viewport window is now frozen when the editor can't be measured reliably, so placeholders stay stable.dragImageProperties on the drag preview. The clone reset its margin to 0 after copying styles, which discarded any margin the user explicitly requested. The reset now runs only when no margin property is listed in dragImageProperties, so the drag image can keep the same spacing as the live block.data-placeholder attribute on empty blocks. The viewport window is now frozen when the editor can't be measured reliably, so placeholders stay stable.6270b99: Ordered lists now support the `type` attribute (a, A, i, I).
6270b99: Ordered lists now support the type attribute (a, A, i, I).
The <ol> type attribute is now fully preserved through the HTML round-trip:
type="a" → lowercase alphabetical markerstype="A" → uppercase alphabetical markerstype="i" → lowercase roman numeral markerstype="I" → uppercase roman numeral markersPaste from external editors (Google Docs, Word, LibreOffice) now correctly detects the list style — both from the HTML type attribute and from CSS list-style-type properties.
Plain text paste of typed ordered list markers (e.g. a. Item, I) Item, i. Item\nii. Item) is detected and converted to the correct list type.
Markdown round-trip preserves typed markers: parsing a. Item creates type: "a", and serializing a typed list back to markdown uses the correct prefix (e.g. I., ii.).
Joining of adjacent lists now respects type — two lists with different types (e.g. default numeric and type="a") are not merged.
6270b99: Ordered lists now support the type attribute (a, A, i, I).
The <ol> type attribute is now fully preserved through the HTML round-trip:
type="a" → lowercase alphabetical markerstype="A" → uppercase alphabetical markerstype="i" → lowercase roman numeral markerstype="I" → uppercase roman numeral markersPaste from external editors (Google Docs, Word, LibreOffice) now correctly detects the list style — both from the HTML type attribute and from CSS list-style-type properties.
Plain text paste of typed ordered list markers (e.g. a. Item, I) Item, i. Item\nii. Item) is detected and converted to the correct list type.
Markdown round-trip preserves typed markers: parsing a. Item creates type: "a", and serializing a typed list back to markdown uses the correct prefix (e.g. I., ii.).
Joining of adjacent lists now respects type — two lists with different types (e.g. default numeric and type="a") are not merged.
6270b99: parseAttributes now supports any word characters at the start of classes or id attributes.
6270b99: Fix marksEqual to compare mark arrays as multisets instead of index-by-index, so order of marks no longer affects the result. Broaden the type signature to accept ProseMirror Mark objects (where type is an object with a name property) alongside the existing JSON mark shape ({ type: string }).
6270b99: Fix a edge-case in rewriteUnknownContent to not fail on null-ish values inside marks or nodes.
@tiptap/pm@3.27.0
isAllowedUri accepting unknown protocols whose name contains a hyphen (e.g. unknown-protocol://test). The hyphen is a valid scheme character per RFC 3986, but the regex was built from a template literal where \- collapsed to -, leaving the terminator class [^a-z+.-:] to parse .-: as a character range that excluded 0-9 and / rather than -. With the proper double-escape, the regex correctly excludes - and unknown hyphenated schemes are rejected again.6270b99: Add props.mount(element) for fully managed popup positioning. It mounts the popup into the configured container (default document.body), keeps it anchored to the cursor, and automatically repositions on scroll, resize, and layout shifts via Floating UI's autoUpdate — no manual listeners required. It returns an unmount function to call in onExit.
This is additive and opt-in: mounting and positioning manually with props.floatingUi + props.clientRect remains supported as an escape hatch.
6270b99: Dismiss managed suggestion popups when the user clicks outside both the popup and the editor. Enabled by default for popups mounted via props.mount, and configurable with the new dismissOnOutsideClick option.
6270b99: Expose Floating UI positioning configuration through SuggestionProps. Consumers can now read placement, offset, flip, container, and a resolved floatingUi config to drive their own positioning loop.
6270b99: Add async suggestion lifecycle handling, including debounced async items(), in-flight request aborting, initialItems, a loading state, and a minQueryLength filter.
marked.use(...) are respected.window.DOMParser or HTMLUnknownElement, so angle-bracket placeholders are preserved as literal text in SSR and Node environments.6270b99: Ordered lists now support the type attribute (a, A, i, I).
The <ol> type attribute is now fully preserved through the HTML round-trip:
type="a" → lowercase alphabetical markerstype="A" → uppercase alphabetical markerstype="i" → lowercase roman numeral markerstype="I" → uppercase roman numeral markersPaste from external editors (Google Docs, Word, LibreOffice) now correctly detects the list style — both from the HTML type attribute and from CSS list-style-type properties.
Plain text paste of typed ordered list markers (e.g. a. Item, I) Item, i. Item\nii. Item) is detected and converted to the correct list type.
Markdown round-trip preserves typed markers: parsing a. Item creates type: "a", and serializing a typed list back to markdown uses the correct prefix (e.g. I., ii.).
Joining of adjacent lists now respects type — two lists with different types (e.g. default numeric and type="a") are not merged.
6270b99: Ordered lists now support the type attribute (a, A, i, I).
The <ol> type attribute is now fully preserved through the HTML round-trip:
type="a" → lowercase alphabetical markerstype="A" → uppercase alphabetical markerstype="i" → lowercase roman numeral markerstype="I" → uppercase roman numeral markersPaste from external editors (Google Docs, Word, LibreOffice) now correctly detects the list style — both from the HTML type attribute and from CSS list-style-type properties.
Plain text paste of typed ordered list markers (e.g. a. Item, I) Item, i. Item\nii. Item) is detected and converted to the correct list type.
Markdown round-trip preserves typed markers: parsing a. Item creates type: "a", and serializing a typed list back to markdown uses the correct prefix (e.g. I., ii.).
Joining of adjacent lists now respects type — two lists with different types (e.g. default numeric and type="a") are not merged.
6270b99: parseAttributes now supports any word characters at the start of classes or id attributes.
6270b99: Fix marksEqual to compare mark arrays as multisets instead of index-by-index, so order of marks no longer affects the result. Broaden the type signature to accept ProseMirror Mark objects (where type is an object with a name property) alongside the existing JSON mark shape ({ type: string }).
6270b99: Fix a edge-case in rewriteUnknownContent to not fail on null-ish values inside marks or nodes.
@tiptap/pm@3.27.0
isAllowedUri accepting unknown protocols whose name contains a hyphen (e.g. unknown-protocol://test). The hyphen is a valid scheme character per RFC 3986, but the regex was built from a template literal where \- collapsed to -, leaving the terminator class [^a-z+.-:] to parse .-: as a character range that excluded 0-9 and / rather than -. With the proper double-escape, the regex correctly excludes - and unknown hyphenated schemes are rejected again.6270b99: Add props.mount(element) for fully managed popup positioning. It mounts the popup into the configured container (default document.body), keeps it anchored to the cursor, and automatically repositions on scroll, resize, and layout shifts via Floating UI's autoUpdate — no manual listeners required. It returns an unmount function to call in onExit.
This is additive and opt-in: mounting and positioning manually with props.floatingUi + props.clientRect remains supported as an escape hatch.
6270b99: Dismiss managed suggestion popups when the user clicks outside both the popup and the editor. Enabled by default for popups mounted via props.mount, and configurable with the new dismissOnOutsideClick option.
6270b99: Expose Floating UI positioning configuration through SuggestionProps. Consumers can now read placement, offset, flip, container, and a resolved floatingUi config to drive their own positioning loop.
6270b99: Add async suggestion lifecycle handling, including debounced async items(), in-flight request aborting, initialItems, a loading state, and a minQueryLength filter.
marked.use(...) are respected.window.DOMParser or HTMLUnknownElement, so angle-bracket placeholders are preserved as literal text in SSR and Node environments.GetSchemaAwarenessDataOptions, getSchemaAwarenessData and SchemaAwarenessDataa38c9c0: Fixed drag-and-drop duplicating blocks during collaboration. When a remote collaborator edited the document mid-drag, dropping left an empty
@tiptap/y-tiptap that restores node range selections across remote updates.@tiptap/y-tiptap to version ^3.0.5@tiptap/y-tiptap to version ^3.0.5@tiptap/y-tiptap to version ^3.0.5@tiptap/y-tiptap to version ^3.0.5@tiptap/y-tiptap that restores node range selections across remote updates.@tiptap/y-tiptap to version ^3.0.5@tiptap/y-tiptap to version ^3.0.5@tiptap/y-tiptap to version ^3.0.5@tiptap/y-tiptap to version ^3.0.57fb19eb: Backspace at the start of a non-first child of a blockquote now lifts that child out, splitting the blockquote around it. A second backspace
Selection extension highlighting beyond the selected text on multi-line selections: the native browser selection is now hidden while the editor is blurred, so only the styled .selection decoration is shown.Selection extension highlighting beyond the selected text on multi-line selections: the native browser selection is now hidden while the editor is blurred, so only the styled .selection decoration is shown.Selection extension highlighting beyond the selected text on multi-line selections: the native browser selection is now hidden while the editor is blurred, so only the styled .selection decoration is shown.711c2e3: Fix: dragging an inline/resizable image within the editor no longer creates a duplicate
711c2e3: Fix: dragging an inline/resizable image within the editor no longer creates a duplicate
When the Image extension was configured with inline: true or resize enabled, dragging an image within the editor could insert a duplicate at the drop position instead of moving it. This happened because the browser's native image drag behavior could populate dataTransfer.files, causing the FileHandler extension to intercept the drop before ProseMirror's internal move logic could run.
711c2e3: Add clearable mark option (default true). unsetAllMarks now skips marks with clearable: false, so semantic marks like comments are not removed by "clear formatting".
711c2e3: Add attrsEqual and marksEqual utility functions to @tiptap/core. attrsEqual compares two attribute objects for equality regardless of key ordering. marksEqual compares two arrays of mark objects by type and attributes using attrsEqual.
711c2e3: Fix plain-text copy of table cell selections including content from unselected cells in between. Each selected range is now serialized independently and joined in document order, so dragging upward (reverse selection) also produces output in document order.
Updated dependencies [711c2e3]
711c2e3: Fix: dragging an inline/resizable image within the editor no longer creates a duplicate
When the Image extension was configured with inline: true or resize enabled, dragging an image within the editor could insert a duplicate at the drop position instead of moving it. This happened because the browser's native image drag behavior could populate dataTransfer.files, causing the FileHandler extension to intercept the drop before ProseMirror's internal move logic could run.
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
711c2e3: Fix: dragging an inline/resizable image within the editor no longer creates a duplicate
When the Image extension was configured with inline: true or resize enabled, dragging an image within the editor could insert a duplicate at the drop position instead of moving it. This happened because the browser's native image drag behavior could populate dataTransfer.files, causing the FileHandler extension to intercept the drop before ProseMirror's internal move logic could run.
711c2e3: Fix HTMLAttributes not being applied to the editor DOM when resize is enabled. The addNodeView path was using only the resolved node attributes and skipping the user-configured HTMLAttributes option. Now it merges them consistently with how renderHTML already works.
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
ListKeymap's Backspace handler now lifts the current list item before merging. At the start of a non-first list item, the item is lifted out of its wrapping list (splitting the list around it) instead of immediately joining its content into the previous item. A second Backspace then hits the existing "paragraph after a list" branch and merges the lifted textblock's content into the previous list's last item. Mirrors the two-step behavior introduced for blockquote in #7891.emptyNodeClass in the Placeholder extension to accept a function that returns a dynamic CSS class name per noderenderJSONContentToReactElement and renderJSONContentToString): you can now pass JSONContent directly and read node fields like node.text in your mappings without type errors or casts.HTMLAttributes not being applied to the <table> element when resizable is disabled (the default). The TableView node view (introduced in 3.23) bypassed renderHTML and never applied user-configured attributes like class or data-* to the rendered table element.prosemirror-tables to ^1.8.0 so findTable is available from @tiptap/pm, and align the related ProseMirror dependencies with the versions required by prosemirror-tables.synced listener registered in onCreate was only removed once the event fired, so destroying the editor first left the createIds closure (and the whole editor it captures) referenced by the shared provider. The listener is now also detached on destroy, so the editor can be garbage collected.a followed by backtick-delimited code) would delete the preceding character. The preceding character is now preserved.onContentError not being triggered when invalid collaborative content is detected. The filterTransaction hook now allows the transaction through to keep ProseMirror state in sync with Yjs, ensuring the contentError event fires correctly.\*, \_, \\) now correctly produces a literal text node, instead of silently dropping the character. On serialization, characters that have special meaning in markdown inline syntax (*, _, `, [, ], \, ~) are now backslash-escaped in non-code text nodes to prevent them from being misinterpreted as formatting delimiters when the output is parsed again.VueNodeViewRenderer thrashing the DOM when contentDOM is null for non-leaf nodes. The renderer now always creates a contentDOM element for non-leaf nodes (matching React's behavior), so ProseMirror has a valid element to render children into even when the NodeView component does not include NodeViewContent or renders it conditionally.VueNodeViewRenderer thrashing the DOM when contentDOM is null for non-leaf nodes. The renderer now always creates a contentDOM element for non-leaf nodes (matching React's behavior), so ProseMirror has a valid element to render children into even when the NodeView component does not include NodeViewContent or renders it conditionally.711c2e3: Fix: dragging an inline/resizable image within the editor no longer creates a duplicate
When the Image extension was configured with inline: true or resize enabled, dragging an image within the editor could insert a duplicate at the drop position instead of moving it. This happened because the browser's native image drag behavior could populate dataTransfer.files, causing the FileHandler extension to intercept the drop before ProseMirror's internal move logic could run.
711c2e3: Add clearable mark option (default true). unsetAllMarks now skips marks with clearable: false, so semantic marks like comments are not removed by "clear formatting".
711c2e3: Add attrsEqual and marksEqual utility functions to @tiptap/core. attrsEqual compares two attribute objects for equality regardless of key ordering. marksEqual compares two arrays of mark objects by type and attributes using attrsEqual.
711c2e3: Fix plain-text copy of table cell selections including content from unselected cells in between. Each selected range is now serialized independently and joined in document order, so dragging upward (reverse selection) also produces output in document order.
Updated dependencies [711c2e3]
711c2e3: Fix: dragging an inline/resizable image within the editor no longer creates a duplicate
When the Image extension was configured with inline: true or resize enabled, dragging an image within the editor could insert a duplicate at the drop position instead of moving it. This happened because the browser's native image drag behavior could populate dataTransfer.files, causing the FileHandler extension to intercept the drop before ProseMirror's internal move logic could run.
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
711c2e3: Fix: dragging an inline/resizable image within the editor no longer creates a duplicate
When the Image extension was configured with inline: true or resize enabled, dragging an image within the editor could insert a duplicate at the drop position instead of moving it. This happened because the browser's native image drag behavior could populate dataTransfer.files, causing the FileHandler extension to intercept the drop before ProseMirror's internal move logic could run.
711c2e3: Fix HTMLAttributes not being applied to the editor DOM when resize is enabled. The addNodeView path was using only the resolved node attributes and skipping the user-configured HTMLAttributes option. Now it merges them consistently with how renderHTML already works.
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
Updated dependencies [711c2e3]
ListKeymap's Backspace handler now lifts the current list item before merging. At the start of a non-first list item, the item is lifted out of its wrapping list (splitting the list around it) instead of immediately joining its content into the previous item. A second Backspace then hits the existing "paragraph after a list" branch and merges the lifted textblock's content into the previous list's last item. Mirrors the two-step behavior introduced for blockquote in #7891.emptyNodeClass in the Placeholder extension to accept a function that returns a dynamic CSS class name per noderenderJSONContentToReactElement and renderJSONContentToString): you can now pass JSONContent directly and read node fields like node.text in your mappings without type errors or casts.HTMLAttributes not being applied to the <table> element when resizable is disabled (the default). The TableView node view (introduced in 3.23) bypassed renderHTML and never applied user-configured attributes like class or data-* to the rendered table element.prosemirror-tables to ^1.8.0 so findTable is available from @tiptap/pm, and align the related ProseMirror dependencies with the versions required by prosemirror-tables.synced listener registered in onCreate was only removed once the event fired, so destroying the editor first left the createIds closure (and the whole editor it captures) referenced by the shared provider. The listener is now also detached on destroy, so the editor can be garbage collected.a followed by backtick-delimited code) would delete the preceding character. The preceding character is now preserved.onContentError not being triggered when invalid collaborative content is detected. The filterTransaction hook now allows the transaction through to keep ProseMirror state in sync with Yjs, ensuring the contentError event fires correctly.\*, \_, \\) now correctly produces a literal text node, instead of silently dropping the character. On serialization, characters that have special meaning in markdown inline syntax (*, _, `, [, ], \, ~) are now backslash-escaped in non-code text nodes to prevent them from being misinterpreted as formatting delimiters when the output is parsed again.VueNodeViewRenderer thrashing the DOM when contentDOM is null for non-leaf nodes. The renderer now always creates a contentDOM element for non-leaf nodes (matching React's behavior), so ProseMirror has a valid element to render children into even when the NodeView component does not include NodeViewContent or renders it conditionally.VueNodeViewRenderer thrashing the DOM when contentDOM is null for non-leaf nodes. The renderer now always creates a contentDOM element for non-leaf nodes (matching React's behavior), so ProseMirror has a valid element to render children into even when the NodeView component does not include NodeViewContent or renders it conditionally.7bd09b7: Fix memory leak when destroying an editor while the collaboration provider stays alive (e.g. multiple editors sharing one provider). The exte
update listener is now removed on destroy, so the editor can be garbage collected.doc.on('destroy', ...) listener that was never removed, keeping the UndoManager — and through it the whole editor — reachable from the long-lived doc. Fixed upstream in @tiptap/y-tiptap; this bumps the dependency to pull in the fix.toText property when serializing the schema into a JSON object, because it is a function and functions are not serializable.@tiptap/pm/inputrules export<enter existing CID here if available> that does not map to any known element is now preserved as literal text instead of disappearing, which also prevents downstream contentMatchAt errors when complex schemas (Mention, Variable, Table) are in use.textDirection editor option in renderToHTMLString, renderToMarkdown, and renderToReactElement, and omit null/undefined attribute values instead of serializing them as the literal string "null".generateTocIds, a server-side utility that assigns id and data-toc-id attributes to anchor nodes in a Tiptap JSON document without needing an Editor instance.update listener is now removed on destroy, so the editor can be garbage collected.doc.on('destroy', ...) listener that was never removed, keeping the UndoManager — and through it the whole editor — reachable from the long-lived doc. Fixed upstream in @tiptap/y-tiptap; this bumps the dependency to pull in the fix.toText property when serializing the schema into a JSON object, because it is a function and functions are not serializable.@tiptap/pm/inputrules export<enter existing CID here if available> that does not map to any known element is now preserved as literal text instead of disappearing, which also prevents downstream contentMatchAt errors when complex schemas (Mention, Variable, Table) are in use.textDirection editor option in renderToHTMLString, renderToMarkdown, and renderToReactElement, and omit null/undefined attribute values instead of serializing them as the literal string "null".generateTocIds, a server-side utility that assigns id and data-toc-id attributes to anchor nodes in a Tiptap JSON document without needing an Editor instance.937ff2e: DragHandle: Added dragImageProperties option to limit which CSS properties are cloned for the drag image. By default all ~300+ computed style
dragImageProperties option to limit which CSS properties are cloned for the drag image. By default all ~300+ computed styles are copied; setting this to a subset (e.g. ['color', 'background-color', 'font-size']) reduces the cost when dragging selections with complex nodes.text* content. The selection is now expanded to include the entire inline node boundaries when deleting, preventing incorrect collapse of inline text nodes.doc.descendants() traversal with a cursor-resolved fast path for the default config and viewport-limited scanning for the non-default config, significantly reducing decoration overhead on large documents.dragImageProperties option to limit which CSS properties are cloned for the drag image. By default all ~300+ computed styles are copied; setting this to a subset (e.g. ['color', 'background-color', 'font-size']) reduces the cost when dragging selections with complex nodes.text* content. The selection is now expanded to include the entire inline node boundaries when deleting, preventing incorrect collapse of inline text nodes.doc.descendants() traversal with a cursor-resolved fast path for the default config and viewport-limited scanning for the non-default config, significantly reducing decoration overhead on large documents.7bf0e73: Fix extra mark tokens after inline atom nodes during Markdown serialization
trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().immediatelyRender: true in client-side Next.js. Previously, when running under Next.js (window.next present), the immediatelyRender option was forced to false even when the user explicitly passed true, breaking client-only Next.js apps that rely on the editor existing on the first render. The hook now only forces false when actual SSR is detected (typeof window === 'undefined'), or when running under Next.js with no explicit value.trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().7bf0e73: Fix $pos() returning correct node for non-text atom nodes instead of doc node
7bf0e73: fix(nodeview): eliminate unnecessary re-renders, add opt-in position tracking
NodeViews no longer re-render when decorations or position change without
content changes. Added trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().
@tiptap/pm@3.23.5
7bf0e73: Respect explicit immediatelyRender: true in client-side Next.js. Previously, when running under Next.js (window.next present), the immediatelyRender option was forced to false even when the user explicitly passed true, breaking client-only Next.js apps that rely on the editor existing on the first render. The hook now only forces false when actual SSR is detected (typeof window === 'undefined'), or when running under Next.js with no explicit value.
7bf0e73: fix(nodeview): eliminate unnecessary re-renders, add opt-in position tracking
NodeViews no longer re-render when decorations or position change without
content changes. Added trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().
Updated dependencies [7bf0e73]
Updated dependencies [7bf0e73]
7bf0e73: fix(nodeview): eliminate unnecessary re-renders, add opt-in position tracking
NodeViews no longer re-render when decorations or position change without
content changes. Added trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().
Updated dependencies [7bf0e73]
Updated dependencies [7bf0e73]
7bf0e73: fix(nodeview): eliminate unnecessary re-renders, add opt-in position tracking
NodeViews no longer re-render when decorations or position change without
content changes. Added trackNodeViewPosition option — when enabled, the
component re-renders on every position shift so calls to getPos() stay
current in render output. Removed the internal nodeViewPositionRegistry.
Added shallow prop comparison in ReactRenderer.updateProps().
Updated dependencies [7bf0e73]
Updated dependencies [7bf0e73]
0f05ae7: Add a new autoTrim option to CharacterCount which can be used to allow exceeding the character limit and either trim or validate the content
autoTrim option to CharacterCount which can be used to allow exceeding the character limit and either trim or validate the content length manuallyautoTrim option to CharacterCount which can be used to allow exceeding the character limit and either trim or validate the content length manually8b210e7: Fix Twitch and YouTube embeds losing their canonical video, clip, channel, or playlist URL when HTML content is loaded back into the editor.
" HTML entity encoding in getHTML() output for inline style attributes. Adds a getStyleProperty utility to @tiptap/core and migrates Color, BackgroundColor, FontFamily, FontSize, LineHeight, and Highlight extensions to use it (#7016)" HTML entity encoding in getHTML() output for inline style attributes. Adds a getStyleProperty utility to @tiptap/core and migrates Color, BackgroundColor, FontFamily, FontSize, LineHeight, and Highlight extensions to use it (#7016)" HTML entity encoding in getHTML() output for inline style attributes. Adds a getStyleProperty utility to @tiptap/core and migrates Color, BackgroundColor, FontFamily, FontSize, LineHeight, and Highlight extensions to use it (#7016)8b210e7: Default immediatelyRender to false in SSR environments instead of throwing an error
Previously, omitting immediatelyRender in an SSR environment (e.g. Next.js) would throw an error in development and silently return null in production. This was a common source of crashes, especially when AI-generated code set up the editor without explicitly passing immediatelyRender: false. The hook now defaults immediatelyRender to true, but automatically sets it to false when SSR is detected, logging a warning in development instead of throwing.
Updated dependencies [8b210e7]
useEditor unmount crash by avoiding replacement of Vue-managed DOM nodes during teardown." HTML entity encoding in getHTML() output for inline style attributes. Adds a getStyleProperty utility to @tiptap/core and migrates Color, BackgroundColor, FontFamily, FontSize, LineHeight, and Highlight extensions to use it (#7016)" HTML entity encoding in getHTML() output for inline style attributes. Adds a getStyleProperty utility to @tiptap/core and migrates Color, BackgroundColor, FontFamily, FontSize, LineHeight, and Highlight extensions to use it (#7016)" HTML entity encoding in getHTML() output for inline style attributes. Adds a getStyleProperty utility to @tiptap/core and migrates Color, BackgroundColor, FontFamily, FontSize, LineHeight, and Highlight extensions to use it (#7016)8b210e7: Default immediatelyRender to false in SSR environments instead of throwing an error
Previously, omitting immediatelyRender in an SSR environment (e.g. Next.js) would throw an error in development and silently return null in production. This was a common source of crashes, especially when AI-generated code set up the editor without explicitly passing immediatelyRender: false. The hook now defaults immediatelyRender to true, but automatically sets it to false when SSR is detected, logging a warning in development instead of throwing.
Updated dependencies [8b210e7]
useEditor unmount crash by avoiding replacement of Vue-managed DOM nodes during teardown.a4e5154: Fix DragHandle unmounts by rendering children into the plugin-managed drag handle element with a React portal.
a4e5154: Fix DragHandle unmounts by rendering children into the plugin-managed drag handle element with a React portal.
This avoids React trying to remove a host node after the drag handle plugin has moved it into its own wrapper.
@tiptap/extension-drag-handle@3.23.1
13b5894: Add selectedOnTextSelection option to node view renderers. When enabled, the selected prop also becomes true when a TextSelection is fully in
selectedOnTextSelection option to node view renderers. When enabled, the selected prop also becomes true when a TextSelection is fully inside the node's range, not only on NodeSelection.selectedOnTextSelection option to node view renderers. When enabled, the selected prop also becomes true when a TextSelection is fully inside the node's range, not only on NodeSelection.selectedOnTextSelection option to node view renderers. When enabled, the selected prop also becomes true when a TextSelection is fully inside the node's range, not only on NodeSelection.selectedOnTextSelection option to node view renderers. When enabled, the selected prop also becomes true when a TextSelection is fully inside the node's range, not only on NodeSelection.onUpdate invocation per document change in the TableOfContents extension.032f8f1: Fix drag handle ghost image alignment when dragging blocks inside offset layouts, and preserve correct ghost image behavior for RTL content.
032f8f1: Fix dependencies installation after packages updates producing peer dependency resolution conflicts
032f8f1: Treat non-indented continuation lines following an ordered list marker as part of the same list item.
This aligns ordered list parsing with CommonMark behavior: lines immediately after a list item (before a blank line) are considered lazy continuation and remain inside the list item rather than ending it.
Updated dependencies [032f8f1]
Updated dependencies [032f8f1]
Updated dependencies [032f8f1]
ac994c5: Fixed insertContentAt corrupting the document when inserting inline content with marks at the start of a paragraph. The from - 1 position adj
insertContentAt corrupting the document when inserting inline content with marks at the start of a paragraph. The from - 1 position adjustment now only applies to block-level content.xmlns="http://www.w3.org/1999/xhtml" attribute from generateHTML output by using innerHTML instead of XMLSerializer for HTML serialization.Fix incorrect selection placement when pasting at the end of a marked text node, ensuring inclusive marks are respected
transaction.setMeta('menuKey', 'show') and transaction.setMeta('menuKey', 'hide')transaction.setMeta('menuKey', 'show') and transaction.setMeta('menuKey', 'hide')flushSync() would run in <EditorContent /> lifecycletransaction.setMeta('menuKey', 'show') and transaction.setMeta('menuKey', 'hide')transaction.setMeta('menuKey', 'show') and transaction.setMeta('menuKey', 'hide')flushSync() would run in <EditorContent /> lifecycletransaction.setMeta('menuKey', 'show') and transaction.setMeta('menuKey', 'hide')Fixed drag handle ghost image for RTL and mixed-direction content: the ghost wrapper now uses the dragged block’s computed direction (via domAtPos), a
direction (via domAtPos), and the drag image hotspot uses the cursor position relative to the ghost wrapper so the preview aligns with the pointer in both LTR and RTL.Use a named import for Suggestion from @tiptap/suggestion to avoid bundler ESM/CJS interop wrapping (__toESM) that caused CJS consumers to receive a m
Use a named import for Suggestion from @tiptap/suggestion to avoid bundler ESM/CJS interop
wrapping (__toESM) that caused CJS consumers to receive a module object instead of the
callable plugin factory.
This is a non-breaking internal fix identical to the one applied to @tiptap/extension-mention
in #6994.
beforeTransaction (whose return value was ignored) to ProseMirror filterTransaction, so invalid collaborative changes are now properly blocked.<, >, &, ") are now decoded to literal characters when parsing markdown into the editor. <, >, and & are re-encoded when serializing back to markdown, while " is preserved as a literal character since double quotes are ordinary in markdown. Code detection for skipping encoding now uses the code: true extension spec instead of hardcoded type names. Literal characters inside code blocks and inline code are always preserved.inline-flex for inline nodes and flex for block nodesattributes = {}. In particular, extendMarkRange('link') no longer extends to adjacent links with different hrefs; restore the previous behavior with extendMarkRange('link', {}).unsetMark removes a mark from inline content that starts at position 0, preventing a RangeError during the before-node lookup.<, >, &, ") are now decoded to literal characters when parsing markdown into the editor. <, >, and & are re-encoded when serializing back to markdown, while " is preserved as a literal character since double quotes are ordinary in markdown. Code detection for skipping encoding now uses the code: true extension spec instead of hardcoded type names. Literal characters inside code blocks and inline code are always preserved.from position is correctly at the start of the double $ signs.renderToggleButton.TableCell and TableHeader nodes now have an align attribute (left, center, right) that is parsed from Markdown column alignment markers (:---, ---:, :---:) and serialized back when rendering to Markdown. Alignment is also parsed from and rendered to HTML via style="text-align: ...".Add a renderToggleButton option to customize the details toggle button, including its accessible label.
renderToggleButton option to customize the details toggle button, including its accessible label.findElementNextToCoords to fall back to view.posAtCoords when elementsFromPoint returns no matching block, resolving the position to the top-level block node.Fix drag preview cleanup so cloned drag images are also removed when dragging ends without a valid drop.
Add support for skipping trailing node insertion on transactions by setting the skipTrailingNode meta flag.
skipTrailingNode meta flag.Fixed Tiptap not publishing with build dist artifacts
Forward BubbleMenu and FloatingMenu HTML props to the actual menu element so attributes like className, style, data-*, and event handlers bind to the
className, style, data-*, and event handlers bind to the positioned menu container.pluginKey is omitted, so multiple BubbleMenu or FloatingMenu components can be mounted without colliding.className, style, data-*, and event handlers bind to the positioned menu container.pluginKey is omitted, so multiple BubbleMenu or FloatingMenu components can be mounted without colliding.isNodeEmpty() so multi-line text with non-whitespace content is no longer treated as empty when ignoreWhitespace is enabled.getEmbedUrlFromYoutubeUrl and isValidYoutubeUrl embed URL utility functionspluginKey is omitted, so multiple BubbleMenu or FloatingMenu components can be mounted without colliding.includeChildren is enabled to prevent duplicate placeholders on wrapper nodes like lists.Improved markdown empty-paragraph roundtripping across top-level and nested block content. Empty paragraphs now serialize with natural blank-line spac
markers for subsequent empty paragraphs at the same level, while parsing preserves those empty paragraphs when converting markdown back to JSON. markers for subsequent empty paragraphs at the same level, while parsing preserves those empty paragraphs when converting markdown back to JSON. markers for subsequent empty paragraphs at the same level, while parsing preserves those empty paragraphs when converting markdown back to JSON. markers for subsequent empty paragraphs at the same level, while parsing preserves those empty paragraphs when converting markdown back to JSON. markers for subsequent empty paragraphs at the same level, while parsing preserves those empty paragraphs when converting markdown back to JSON.Fix tilde-fenced code blocks (~~~) being silently dropped when parsing markdown
~~~) being silently dropped when parsing markdowntypes: 'all' in UniqueID to target every node type except doc and text.style parsing in mergeAttributes for values containing : or ; (e.g. url(https://...) or url(data:...;charset=...,)) and skip incomplete declarationsAdd transformPastedHTML extension API that allows extensions to transform pasted HTML content before it's parsed into the editor, enabling cleanup of
transformPastedHTML extension API that allows extensions to transform pasted HTML content before it's parsed into the editor, enabling cleanup of styles, removal of dangerous content, and modification of pasted HTML through a chainable transform system.types: use '*' to apply to all nodes and marks, 'nodes' for all nodes (excluding text), or 'marks' for all marks.editor.view instead of empty string when editor is emptyBubbleMenu/FloatingMenu to use pluginKey as the transaction meta key so that multiple instances can be updated independently without affecting each otherBubbleMenu/FloatingMenu to use pluginKey as the transaction meta key so that multiple instances can be updated independently without affecting each other@tiptap/react/menus entrypoint to keep floating-ui optionalBubbleMenu/FloatingMenu to use pluginKey as the transaction meta key so that multiple instances can be updated independently without affecting each otherCodeBlockLowlight by switching the CodeBlock import to a named export. This prevents extend is not a function errors caused by ESM/CJS interop issues.BubbleMenu/FloatingMenu to use pluginKey as the transaction meta key so that multiple instances can be updated independently without affecting each otherAdd title attribute to Link extension. The title can now be set via setLink and toggleLink commands and is rendered as an HTML title attribute.
title attribute to Link extension. The title can now be set via setLink and toggleLink commands and is rendered as an HTML title attribute.[text](url "title").@tiptap/react/menus entrypoint to keep floating-ui optionalThe old imperative setup will remain fully backward-compatible for this major release. We plan to deprecate (and remove) the legacy setup in the next…
Introduce a new, optional React integration that provides a declarative <Tiptap /> component for setting up editors in React apps.
Summary
<Tiptap /> components. This is an additive change and does not remove or change existing APIs.Why this change
Migration and usage
<Tiptap /> component and migrate when convenient.Example
import { Tiptap, useEditor } from '@tiptap/react'
function MyEditor() {
const editor = useEditor({ extensions: [StarterKit], content: '<h1>Hello from Tiptap</h1>' })
return (
<Tiptap instance={editor}>
<Tiptap.Content />
<Tiptap.BubbleMenu>My Bubble Menu</Tiptap.BubbleMenu>
<Tiptap.FloatingMenu>My Floating Menu</Tiptap.FloatingMenu>
<MenuBar /> {/* MenuBar can use the new `useTiptap` hook to read the editor instance from context */}
</Tiptap>
)
}
Deprecation plan
dataAttribute to the extension option to control which attribute name will be used for the placeholder label.Fixed markdown serialization doubling newlines and parsing collapsing multiple blank lines
$nodes() method to correctly return inline nodes (like text, mention, etc.) by fixing the children getter in NodePos classAdded a safeguard to avoid TypeError: Cannot read properties of null (reading 'domFromPos') being thrown when the editor was being destroyed
TypeError: Cannot read properties of null (reading 'domFromPos') being thrown when the editor was being destroyedisFirefox utility to coreTypeError: Cannot read properties of null (reading 'domFromPos') being thrown when the editor was being destroyedAdd a native audio extension with demos and tests.
setMeta('floatingMenu', 'updatePosition')setMeta('floatingMenu', 'updatePosition')setMeta('floatingMenu', 'updatePosition')setMeta('floatingMenu', 'updatePosition')findDuplicates helper performance from O(n²) to O(n) by using Set-based lookups instead of Array.indexOfFix Safari scrolling to top when using editor.chain().focus() commands
Prevent auto-linking of bare hostnames (e.g., localhost) and IP addresses without a protocol prefix
localhost) and IP addresses without a protocol prefixappendTo prop in BubbleMenu and FloatingMenuappendTo prop in BubbleMenu and FloatingMenuAdd a new shouldShow callback to the Suggestion utility. This allows developers to filter when suggestions are displayed, which is especially useful f
shouldShow callback to the Suggestion utility. This allows developers to filter when suggestions are displayed, which is especially useful for collaborative environments to prevent suggestions from popping open for remote users.Add a new dispatchTransaction hook to extensions, allowing developers to intercept, modify, or block transactions before they are applied to the edito
dispatchTransaction hook to extensions, allowing developers to intercept, modify, or block transactions before they are applied to the editor state.Add new Twitch extension for embedding Twitch videos, clips, and live channels in the editor. Supports customizable parameters like autoplay, muted, a
Add new Twitch extension for embedding Twitch videos, clips, and live channels in the editor. Supports customizable parameters like autoplay, muted, and start time, with attribute-level overrides for per-embed configuration.
Documentation: https://tiptap.dev/docs/editor/extensions/nodes/twitch
Append all children of editors parent node to element
Fixes a regression introduced by #6972, that resulted in elements that got appended to the editors parent node staying detached. E.g. the drag handle plugin is affected by this regression.
Append all children of editors parent node to element
Fixes a regression introduced by #6972, that resulted in elements that got appended to the editors parent node staying detached. E.g. the drag handle plugin is affected by this regression.
Append all children of editors parent node to element
Fixes a regression introduced by #6972, that resulted in elements that got appended to the editors parent node staying detached. E.g. the drag handle plugin is affected by this regression.
findElementNextToCoords to resolve the parent when nodeAt(pos) is null
(e.g., inside an atom node that allows inline content)Added data-dragging attribute to drag handle elements to track drag state.
data-dragging attribute to drag handle elements to track drag state.data-dragging attribute to drag handle elements to track drag state.data-dragging attribute to drag handle elements to track drag state.data-dragging attribute to drag handle elements to track drag state.enableClickSelection works regardless of the openOnClick option by always registering the link click handler plugin.createCustomHandle callback to ResizableNodeView, allowing developers to fully customize resize handles. When provided, it replaces the default handle creation and bypasses the built-in positionHandle logic, giving complete control over markup, styling, and positioning while preserving backward compatibility.wrapper element to better support dynamic alignment. This eliminates the need for !important overrides in user styles.update event listener to dynamically attach or remove resize handles based on the editor’s editable state. The implementation tracks the previous editable state to avoid unnecessary re-renders.createCustomHandle callback to ResizableNodeView, allowing developers to fully customize resize handles. When provided, it replaces the default handle creation and bypasses the built-in positionHandle logic, giving complete control over markup, styling, and positioning while preserving backward compatibility.wrapper element to better support dynamic alignment. This eliminates the need for !important overrides in user styles.update event listener to dynamically attach or remove resize handles based on the editor’s editable state. The implementation tracks the previous editable state to avoid unnecessary re-renders.Fixed a bug that mutated the ProseMirror document during server-side rendering, which could cause "Invalid content for node doc" errors.
Implement position mapping using the MappablePosition class. This enables position mapping in collaborative editing scenarios.
Implement position mapping using the MappablePosition class. This enables position mapping in collaborative editing scenarios.
MappablePosition class in core with position, fromJSON, and toJSON methodseditor.utils property with getUpdatedPosition(position, transaction) and createMappablePosition() methodsCollaborationMappablePosition subclass that extends MappablePosition with Y.js relative position supportImplement position mapping using the MappablePosition class. This enables position mapping in collaborative editing scenarios.
MappablePosition class in core with position, fromJSON, and toJSON methodseditor.utils property with getUpdatedPosition(position, transaction) and createMappablePosition() methodsCollaborationMappablePosition subclass that extends MappablePosition with Y.js relative position supportthis.options in the updateUser command. this.options can be a getter and is not writable; the command now updates the provider awareness directly so user updates are applied correctly.fast-deep-equal dependency with maintained fast-equalsthis.getPos() when ProseMirror and React render cycles got out of sync, which could cause errors during updates.Improve TypeScript generics for Node.extend
Improve TypeScript generics for Node.extend
The Node.extend method's TypeScript signature was updated so that ExtendedConfig can extend NodeConfig and MarkConfig, improving type inference when extending Node and Mark classes with additional config properties.
This is a type-only change — there are no runtime behavior changes.
Add native text direction support for RTL and bidirectional content. The editor now includes a textDirection option that can be set to 'ltr', 'rtl', o
textDirection option that can be set to 'ltr', 'rtl', or 'auto' to control the direction of all content globally. Additionally, new setTextDirection and unsetTextDirection commands allow for granular control of text direction on specific nodes. This enables proper rendering of right-to-left languages like Arabic and Hebrew, as well as bidirectional text mixing multiple languages.Fixed a bug that caused extra characters to be inserted after a parsed, nestable content block by accounting for leading newlines
undefined as a value for the default attribute keyupdateAttributes and resetAttributes commands to return accurate results when used with .can(). Previously, these commands would always return true even when they couldn't perform the operation. Now they correctly return false when no matching nodes or marks are found in the selection.setTextAlign and unsetTextAlign commands to work correctly with .can() checks. Changed logic from .every() to .some() to return true when at least one configured node type matches, rather than requiring all types to match.marked dependency from v16 to v15.Your coding agent can read these notes before it upgrades. Set up the MCP server →