@platejs/docx-io
Bug Fixes
-
Skip remote image URLs by default during DOCX export. (#5053)
Migration: Convert trusted remote images to data URIs before calling
htmlToDocxBlob, or passallowRemoteImages: trueonly when the HTML source is trusted.
@platejs/link
Bug Fixes
df0996f810d69f1d6660dbeac2a4cceda1d5e6dd– Fix Floating Link: ignore the Safari + Japanese IME composition-commit Enter (keyCode === 229, whereisComposingis false) so it no longer submits the link mid-composition. Mirrors the Enter guard already used incmdk.
@platejs/ai
Features
34fdca42f73e7dd30d94db31058c28662dbb9212– Support AI SDK v7 chat helper types.
@platejs/core
Bug Fixes
- Keep dependent Plate packages aligned with core patch releases. (#5046)
platejs
Bug Fixes
- Updated
@platejs/core,@platejs/utils.
@platejs/utils
Bug Fixes
- Updated
@platejs/core.
@platejs/ai
Bug Fixes
- Updated
@platejs/selection.
@platejs/selection
Bug Fixes
- Fix block selection copy and cut in Safari by writing selected blocks to the native clipboard event. (#5018)
@platejs/ai
Bug Fixes
- Updated
@platejs/markdown.
@platejs/markdown
Bug Fixes
- Fix markdown deserialization from crashing on malformed HTML-like MDX input. (#5016)
@platejs/list
Bug Fixes
- Fix ordered paragraph lists continuing numbering from numbered headings. (#5011)
@platejs/core
Bug Fixes
- Fix complex leaf and text renderers triggering React hook-order warnings when marks change. (#5009)
platejs
Bug Fixes
- Updated
@platejs/core,@platejs/utils.
@platejs/utils
Bug Fixes
- Updated
@platejs/core.
@platejs/ai
Bug Fixes
- Updated
@platejs/markdown.
@platejs/markdown
Bug Fixes
- Fix MDX fallback deserialization for GFM tables containing plain less-than text. (#5007)
@platejs/docx-io
Features
-
Forward two dropped options in
exportToDocx: (#4997)pageSize— the html-to-docx engine accepts a page size, butexportToDocxonly forwardedmarginsandorientation, so the document was always the default (US Letter). You can now pass e.g.pageSize: { width: 11906, height: 16838 }to export A4.fontFamily— it was only applied to the serialized HTML (and only when anEditorStaticComponentwas provided), so the document default font was never set and Word fell back to Times New Roman. It now also sets the document default font (documentOptions.font).
@platejs/dnd
Bug Fixes
- Clear stale DnD drop indicators when dragging from a block into editor whitespace. (#5002)
@platejs/ai
Bug Fixes
- Updated
@platejs/table.
@platejs/table
Bug Fixes
- Remove the table when deleting its final column. (#4994)
@platejs/docx-io
Bug Fixes
- Fix
exportToDocxadding blank paragraphs at the top of the document.wrapHtmlForDocxemitted a<!DOCTYPE html>and indented the template; html-to-docx (html-to-vdom) keeps the DOCTYPE and the whitespace-only text nodes between tags and renders each as a blank paragraph. The wrapper now emits tight markup with no DOCTYPE. (#4991)
@platejs/ai
Bug Fixes
- Updated
@platejs/table.
@platejs/core
Bug Fixes
-
Add
transformInitialValueandnodeId.initialValueIdswhile keepingnormalizeInitialValueas a deprecated alias (#4987) -
Improve large-document mount and render performance across core element, mark, and
nodeIdpaths (#4987)- Cut
10kmixed-document core mount time from1240.60 msto468.26 mswithoutnodeId(62.3%,2.65xfaster) - Cut
10kmixed-document core mount time from1290.66 msto477.73 mswithnodeId(63.0%,2.70xfaster) - Cut mixed-document
nodeIdoverhead over core from+50.06 msto+9.46 ms(81.1%smaller) - Cut duplicate-id paste cost from
20.06 msto13.79 ms(31.2%,1.45xfaster) - Cut
10kcode-only mount time from1500.30 msto496.47 ms(66.9%,3.02xfaster) and shrink the code-only tax over core from+280.75 msto+27.89 ms(90.1%smaller) - Bring the current
10kcore and basic large-document mount lanes to Slate parity or better (core -3.5%,core + nodeId -1.6%,basic -1.2%) - Preserve Slate children for void
render.astags and Slate attributes on simple leaf/text render paths
- Cut
platejs
Bug Fixes
- Updated
@platejs/core,@platejs/slate,@platejs/utils.
@platejs/slate
Bug Fixes
-
Add experimental
editor.tf.setNodesBatchfor exact-path node prop updates on large documents (#4987)- Cut the large-document
set_nodehot path from18.56 msto2.63 msat1kblocks (7.05xfaster) - Cut the same path from
118.54 msto4.92 msat5kblocks (24.10xfaster) - Let
@platejs/corebatch livenodeIdnormalization instead of paying onesetNodescall per missing id - Keep
editor.tf.setNodesBatchexplicitly temporary. It is experimental and will be removed in a future release
- Cut the large-document
-
Updated
slate-hyperscript. (231b986)
@platejs/table
Bug Fixes
- Speed up unmerged table range selection while preserving merged-cell handling (#4987)
@platejs/utils
Bug Fixes
- Updated
@platejs/core,@platejs/slate.
@platejs/core
Bug Fixes
-
Fix
.configure({ inputRules })losing rules on subsequent editor instances (#4983)The user's config object was shared across resolutions via closure; clearing
inputRuleson the first resolve left later editors (StrictMode remounts, HMR, multi-editor pages) with no configured rules. -
Fix
createTextSubstitutionInputRulenot firing on the final character of flat matches (e.g.->→→,(c)→©) (#4983)
platejs
Bug Fixes
- Updated
@platejs/core,@platejs/utils.
@platejs/utils
Bug Fixes
- Updated
@platejs/core.
platejs
Bug Fixes
- Updated
@platejs/core,@platejs/slate,@platejs/utils.
@platejs/slate
Bug Fixes
- Updated
slate-react. (ce9ec87)
@platejs/utils
Bug Fixes
- Updated
@platejs/core,@platejs/slate.
@platejs/ai
Bug Fixes
- Updated
@platejs/markdown.
@platejs/markdown
Bug Fixes
- Respect
resourceLinkwhen serializing bare autolink literals (#4972)
CHANGELOG · v53.0.3...v53.0.4 · By @ajmnz
@platejs/ai
Bug Fixes
- Clear block streaming state when
aiChat.stop()stops generation (#4945)
@platejs/link
Bug Fixes
- Fix empty link normalization when suggestion acceptance removes the last link character (#4945)
platejs
Bug Fixes
- Updated
@platejs/utils.
@platejs/suggestion
Bug Fixes
- Fix inline-void delete and replace suggestions around mentions and paragraph boundaries (#4945)
@platejs/utils
Bug Fixes
- Add a trailing-block insert hook for normalization-driven insert behavior (#4945)
@platejs/list
Bug Fixes
- Fix
normalizeListStartto skip unordered list items and resume ordered list numbering past same-indent unordered siblings (#4954)
CHANGELOG · v53.0.1...v53.0.2 · By @dylans
@platejs/media
Bug Fixes
- Fix video URL parsing to avoid ReDoS on crafted time parameters (#4957)
CHANGELOG · v53.0.0...v53.0.1 · By @zbeyens
@platejs/autoformat
Breaking Changes
-
Deprecate
@platejs/autoformat. Markdown shortcuts and text substitutions are now authored asinputRuleson each feature plugin, andAutoformatPluginremains only as an inert compatibility export. (#4941)Migration:
- Remove
AutoformatPluginfrom your plugins and replace@platejs/autoformatafter migrating rules. - Replace each old
AutoformatRulewith the matching rule factory on the plugin that owns the feature. See the table below. - Replace symbol substitutions (arrows, fractions, smart quotes, legal, math operators) with
createTextSubstitutionInputRuleregistered on a localcreateSlatePlugin. - Replace
rules[].querywithenabledon the rule factory call. Replace the global code-block guard with a per-pluginenabledcheck. - Drop
enableUndoOnDelete— undo-on-delete is the built-in behavior. - Replace custom
AutoformatRuledefinitions withcreateRuleFactoryfromplatejs.
// Before import { AutoformatPlugin } from "@platejs/autoformat"; const editor = createPlateEditor({ plugins: [ AutoformatPlugin.configure({ options: { enableUndoOnDelete: true, rules: [ { match: "# ", mode: "block", type: KEYS.h1 }, { match: "**", mode: "mark", type: KEYS.bold }, { match: "* ", mode: "block", type: "list", format: (editor) => toggleList(editor, { listStyleType: KEYS.ul }), }, ], }, }), ], }); // After import { BoldRules } from "@platejs/basic-nodes"; import { BoldPlugin } from "@platejs/basic-nodes/react"; import { HeadingRules } from "@platejs/basic-nodes"; import { H1Plugin } from "@platejs/basic-nodes/react"; import { BulletedListRules } from "@platejs/list"; import { ListPlugin } from "@platejs/list/react"; const editor = createPlateEditor({ plugins: [ H1Plugin.configure({ inputRules: [HeadingRules.markdown()] }), BoldPlugin.configure({ inputRules: [BoldRules.markdown({ variant: "*" })], }), ListPlugin.configure({ inputRules: [BulletedListRules.markdown({ variant: "-" })], }), ], });Rule Map
Basic blocks —
@platejs/basic-nodesOld rule New rule { match: '# '..'###### ', mode: 'block', type: KEYS.h1..h6 }HxPlugin.configure({ inputRules: [HeadingRules.markdown()] })— register on eachH1Plugin..H6Plugin{ match: '> ', mode: 'block', type: KEYS.blockquote }BlockquotePlugin.configure({ inputRules: [BlockquoteRules.markdown()] }){ match: ['---', '—-', '___ '], mode: 'block', type: KEYS.hr }HorizontalRulePlugin.configure({ inputRules: [HorizontalRuleRules.markdown({ variant: '-' }), HorizontalRuleRules.markdown({ variant: '_' })] })Basic marks —
@platejs/basic-nodesOld rule New rule Owning plugin { match: '**', mode: 'mark', type: KEYS.bold }BoldRules.markdown({ variant: '*' })BoldPlugin{ match: '__', mode: 'mark', type: KEYS.underline }UnderlineRules.markdown()UnderlinePlugin{ match: '*', mode: 'mark', type: KEYS.italic }ItalicRules.markdown({ variant: '*' })ItalicPlugin{ match: '_', mode: 'mark', type: KEYS.italic }ItalicRules.markdown({ variant: '_' })ItalicPlugin{ match: '`', mode: 'mark', type: KEYS.code }CodeRules.markdown()CodePlugin{ match: '~~', mode: 'mark', type: KEYS.strikethrough }StrikethroughRules.markdown()StrikethroughPlugin{ match: '~', mode: 'mark', type: KEYS.sub }SubscriptRules.markdown()SubscriptPlugin{ match: '^', mode: 'mark', type: KEYS.sup }SuperscriptRules.markdown()SuperscriptPlugin{ match: '==', mode: 'mark', type: KEYS.highlight }HighlightRules.markdown({ variant: '==' })HighlightPlugin{ match: '≡', mode: 'mark', type: KEYS.highlight }HighlightRules.markdown({ variant: '≡' })HighlightPlugin{ match: '***', mode: 'mark', type: [bold, italic] }MarkComboRules.markdown({ variant: 'boldItalic' })BoldPlugin{ match: '__*', mode: 'mark', type: [underline, italic] }MarkComboRules.markdown({ variant: 'italicUnderline' })BoldPlugin{ match: '__**', mode: 'mark', type: [underline, bold] }MarkComboRules.markdown({ variant: 'boldUnderline' })BoldPlugin{ match: '___***', mode: 'mark', type: [underline, bold, italic] }MarkComboRules.markdown({ variant: 'boldItalicUnderline' })BoldPluginRegister each family on its owning plugin:
BoldPlugin.configure({ inputRules: [ BoldRules.markdown({ variant: "*" }), BoldRules.markdown({ variant: "_" }), MarkComboRules.markdown({ variant: "boldItalic" }), MarkComboRules.markdown({ variant: "boldUnderline" }), MarkComboRules.markdown({ variant: "boldItalicUnderline" }), MarkComboRules.markdown({ variant: "italicUnderline" }), ], });Code block —
@platejs/code-blockOld rule New rule { match: '```', mode: 'block', type: KEYS.codeBlock, format: insertEmptyCodeBlock }CodeBlockPlugin.configure({ inputRules: [CodeBlockRules.markdown({ on: 'match' })] })Lists —
@platejs/listand@platejs/list-classicOld rule New rule { match: ['- ', '* '], mode: 'block', format: toggleList(..., { listStyleType: KEYS.ul }) }BulletedListRules.markdown({ variant: '-' }),BulletedListRules.markdown({ variant: '*' }){ match: /^\d+\.$ |^\d+\)$ /, matchByRegex: true, format: toggleList(..., { listStyleType: KEYS.ol }) }OrderedListRules.markdown({ variant: '.' }),OrderedListRules.markdown({ variant: ')' }){ match: '[] ', mode: 'block', format: toggleList(..., { listStyleType: KEYS.listTodo }) }TaskListRules.markdown({ checked: false }){ match: '[x] ', mode: 'block', format: toggleList + setNodes({ checked: true }) }TaskListRules.markdown({ checked: true })ListPlugin.configure({ inputRules: [ BulletedListRules.markdown({ variant: "-" }), BulletedListRules.markdown({ variant: "*" }), OrderedListRules.markdown({ variant: "." }), OrderedListRules.markdown({ variant: ")" }), TaskListRules.markdown({ checked: false }), TaskListRules.markdown({ checked: true }), ], });Replace
@platejs/listwith@platejs/list-classicimports when using the classic list model. The factory names are identical.Math —
@platejs/mathOld rule New rule Inline equation $…$InlineEquationPlugin.configure({ inputRules: [MathRules.markdown({ variant: '$' })] })Block equation $$…$$EquationPlugin.configure({ inputRules: [MathRules.markdown({ on: 'break', variant: '$$' })] })Link —
@platejs/linkOld behavior New rule [text](url)markdownLinkRules.markdown()Autolink on paste LinkRules.autolink({ variant: 'paste' })Autolink on space LinkRules.autolink({ variant: 'space' })Autolink on Enter LinkRules.autolink({ variant: 'break' })LinkPlugin.configure({ inputRules: [ LinkRules.markdown(), LinkRules.autolink({ variant: "paste" }), LinkRules.autolink({ variant: "space" }), LinkRules.autolink({ variant: "break" }), ], });Text substitutions (arrows, fractions, legal, math operators, smart quotes)
Move these to a local
createSlatePluginwithcreateTextSubstitutionInputRule:import { createSlatePlugin, createTextSubstitutionInputRule, KEYS, } from "platejs"; const isTextSubstitutionBlocked = (editor) => editor.api.some({ match: { type: [editor.getType(KEYS.codeBlock)] } }); const ShortcutsPlugin = createSlatePlugin({ key: "shortcuts", inputRules: [ createTextSubstitutionInputRule({ enabled: ({ editor }) => !isTextSubstitutionBlocked(editor), patterns: [ { format: "→", match: "->" }, { format: "⇒", match: "=>" }, { format: "½", match: "1/2" }, { format: "™", match: ["(tm)", "(TM)"] }, { format: ["“", "”"], match: '"' }, ], }), ], });Each pattern set is just data —
autoformatArrow,autoformatLegal,autoformatMath,autoformatPunctuation,autoformatSmartQuotes, andautoformatLegalHtmlfrom the old package map 1:1 ontopatternsarrays.AutoformatKitin the Plate registry is pre-built with all of them.Custom rules
Old
AutoformatRuleobjects have no direct replacement. Build a rule family withcreateRuleFactory:import { createRuleFactory } from "platejs"; const MyRules = { markdown: createRuleFactory({ type: "blockMatch", match: "!! ", format: "my-block", }), }; MyPlugin.configure({ inputRules: [MyRules.markdown()] });Option removals
enableUndoOnDelete— removed. Backspace on a rule-inserted node restores the source text by default.rules[].query— replaced byenabledon the rule factory call.rules[].preFormat/rules[].format— replaced by rule-familyformatandresolvecallbacks insidecreateRuleFactory.rules[].trigger— rule families set their own trigger. Override it with thetriggeroption on a customcreateRuleFactorycall.
See the Autoformat doc for the kit path and the Plugin Input Rules guide for the full runtime.
- Remove
@platejs/basic-nodes
Breaking Changes
-
Store blockquotes as container blocks with block children. Lift every selected nested quoted block one level on
Shift+Tab. Reset headings to paragraphs onBackspaceat block start before any merge. (#4941)Migration:
- Update persisted values, fixtures, and tests to use block children instead of direct text children.
- Expect
editor.tf.blockquote.toggle()to wrap or unwrap blocks instead of retagging one text block in place. - Empty later quoted paragraphs delete in place on
Backspaceinstead of jumping out of the quote. Backspaceat the start of a heading now resets the heading to a paragraph before any merge.- Legacy flat blockquote values still normalize on load, but persisted snapshots and fixtures should move to the new shape.
// Before { type: 'blockquote', children: [{ text: 'Quote' }] } // After { type: 'blockquote', children: [{ type: 'p', children: [{ text: 'Quote' }] }], }
@platejs/code-block
Breaking Changes
- Keep
Backspaceat the start of a non-empty first code line inside the code block. Merge an empty inner code line into the previous code line instead of unwrapping the block. (#4941)
@platejs/markdown
Breaking Changes
-
Round-trip blockquotes as nested block content instead of flat newline-packed text. Serialize image titles from
node.titleinstead of copying the caption into the markdown title slot. Preserve MDX media attribute expressions during markdown serialization instead of stringifying them into JSON text. Serialize plain URL links back to bare URL markdown instead of bracket-link form. Round-trip footnote references and definitions as dedicated footnote nodes instead of collapsing them to plain-text fallback. (#4941)Migration:
- Update snapshots and direct value assertions to expect
blockquote.childrento contain block nodes such as paragraphs and lists. - If you generate initial editor values from markdown, hydrate blockquotes with paragraph children instead of flat text.
- If you want markdown output like
, setnode.title. Images without a title now serialize as. - If you serialize MDX media nodes with expression attributes like
width={640}, expect those expressions to stay as expressions instead of turning into quoted JSON. - Plain URL links such as
https://platejs.orgnow serialize as bare URLs instead of[https://platejs.org](https://platejs.org). - If you enable footnote-aware markdown input, install
@platejs/footnoteand includeBaseFootnoteReferencePluginandBaseFootnoteDefinitionPluginso footnote nodes have real editor semantics instead of falling back to unknown node types.
// Before { type: 'blockquote', children: [{ text: 'Quote\\nNext line' }] } // After { type: 'blockquote', children: [ { type: 'p', children: [{ text: 'Quote' }] }, { type: 'p', children: [{ text: 'Next line' }] }, ], } - Update snapshots and direct value assertions to expect
Bug Fixes
-
Write canonical date nodes as
<date value="..."/>and round-trip normalized media embed metadata (#4941) -
Preserve unknown MDX and raw HTML block source more faithfully during markdown deserialization fallback (#4941)
@platejs/table
Breaking Changes
- Escalate the second
selectAllfrom the current table to the whole document. (#4941)
@platejs/core
Features
- Add
liftas a break and delete rule action for blocks that should leave one ancestor level instead of resetting or exiting. Reset the trailing block to a paragraph whensplitResethandles selected heading text. (#4941)
Bug Fixes
-
Add
createRuleFactoryfor building input rule families with overridable defaults and required options (#4941) -
Add
useNavigationHighlight(path)for React node components that need the current navigation-feedback target without reading plugin options directly (#4941)
@platejs/footnote
Features
- Add
FootnoteReferencePlugin,FootnoteDefinitionPlugin, andFootnoteInputPluginfor real footnote nodes and inline[^combobox insertion in Plate editors. (#4941)
@platejs/date
Bug Fixes
- Store date nodes as canonical
YYYY-MM-DDvalues and preserve unparseable legacy text as fallback data (#4941)
@platejs/link
Bug Fixes
- Keep pasted URLs literal inside markdown link source entry by default (#4941)
@platejs/list
Bug Fixes
- Allow list markdown rule families to override shared runtime rule fields while keeping semantic
variantandcheckedoptions (#4941)
@platejs/list-classic
Bug Fixes
- Allow classic list markdown rule families to override shared runtime rule fields while keeping semantic
variantandcheckedoptions (#4941)
@platejs/media
Bug Fixes
-
Support allowlisted Twitter/X embed snippet extraction in media embed URL transforms (#4941)
-
Normalize supported media embeds into canonical provider metadata and preserve source URLs for embed editing (#4941)
@platejs/slate
Bug Fixes
- Updated
slate,slate-dom. (081cbe9)
@platejs/toc
Bug Fixes
-
Add active section state to
useTocElementStateso TOC elements can mark the current heading while the document scrolls (#4941) -
Fix TOC activation to navigate without entering block-selection mode (#4941)
@platejs/utils
Bug Fixes
- Add
KEYS.footnoteDefinition,KEYS.footnoteReference, andKEYS.footnoteInput(#4941)
CHANGELOG · v52.3.22...v53.0.0 · By @zbeyens, @github-actions[bot]