v0.52.0

Graphify-Labs/graphifyv0.52.0Sep 28, 2026by etrepum

Upgrading?

AI Summary

Lexical v0.52.0 targets mobile text input fixes and moves towards Lexical 1.0 with markdown extension unification and headless editor support.

Key Highlights

  • Unified Markdown configuration with MdastExtension
  • HeadlessExtension for server-side processing and tests
  • Selection timing changes with SELECTION_CHANGE_COMMAND
  • Fixes for deletion, drag-and-drop, and mobile input

Breaking Changes

  • SELECTION_CHANGE_COMMAND consistently runs before DOM reconciliation
  • Default HTML export now honors DOMRenderExtension overrides
  • LexicalNode.getCommonAncestor is removed

New Features

  • Unified Markdown configuration and inherited export rules
  • Import/export middleware through context.next()
  • HeadlessExtension for buildEditorFromExtensions

Full Release Notes

v0.52.0 is an ad-hoc release targeting a few fixes to mobile text input, but carries a number of other updates accumulated in the past week as we move towards Lexical 1.0. We did a pass to modernize the documentation focusing on the latest features. `@lexical/mdast` now uses one configurable `MdastExtension` for import and export, headless editors can be built entirely from extensions, and selection notifications have a consistent timing contract. There are also fixes for deletion, horizontal caret scrolling, drag-and-drop and mobile input.

### Breaking Changes

*   [`lexical`](https://lexical.dev/docs/api/modules/lexical) — **`SELECTION_CHANGE_COMMAND` consistently runs before DOM reconciliation**, inside the pending writable update. This includes automatic `NodeSelection` and `TableSelection` notifications that previously ran after reconciliation. `$getSelection()` returns the pending selection; `$getPreviousSelection()` returns the last committed selection. Eligible programmatic changes now notify without waiting for a native `selectionchange` event. Listener edits join the same commit, and handlers that change the selection must converge. A listener that throws can now abort the whole pending update, including the edit that triggered the notification, and listeners inherit that update's tags, so content edits made during a `COLLABORATION_TAG` or `HISTORIC_TAG` update are not synced to Yjs peers. Move DOM-dependent work such as element lookup, focus checks and floating UI positioning into `$onUpdate(() => editor.read('latest', () => { /* ... */ }))`, reading selection and nodes inside that callback. Use `registerUpdateListener` for UI that must respond to every commit, including content-only changes ([#9219](https://github.com/facebook/lexical/pull/9219), [selection timing guide](https://lexical.dev/docs/concepts/selection#selection-change-timing))
*   [`lexical`](https://lexical.dev/docs/api/modules/lexical) / [`@lexical/html`](https://lexical.dev/docs/api/modules/lexical_html) — **Default HTML export now honors `DOMRenderExtension` `$createDOM` overrides.** This affects nodes that inherit `LexicalNode.exportDOM` or call `super.exportDOM(editor)`, including text and paragraphs. If an override was intended only for the live editor, review the HTML it now exports. Custom `exportDOM` implementations that create their own elements can still bypass this hook; `$updateDOM` and `$decorateDOM` remain reconciliation-only, and `$exportDOM` handles export-specific changes ([#9231](https://github.com/facebook/lexical/pull/9231))
*   [`lexical`](https://lexical.dev/docs/api/modules/lexical) — **`LexicalNode.getCommonAncestor` is removed.** Use `$getCommonAncestor(a, b)?.commonAncestor ?? null`. This is not quite a direct substitution: the removed method first replaced non-element arguments with their parents, while the function compares the supplied nodes and can return a text node when both arguments are that node. Pass the parents of non-element nodes if you need the old behavior, and use type guards instead of the old unchecked generic type argument ([#9238](https://github.com/facebook/lexical/pull/9238))

### Deprecations

*   [`@lexical/mdast`](https://lexical.dev/docs/api/modules/lexical_mdast) — `MdastImportExtension` and `MdastExportExtension` are deprecated aliases of `MdastExtension`. Move configuration to `MdastExtension` and replace `MdastImportExtensionOutput` / `MdastExportExtensionOutput` with `MdastExtensionOutput`. The package is experimental, and these aliases are intended to remain for only one release ([#9198](https://github.com/facebook/lexical/pull/9198))

### New APIs & Features

*   [`@lexical/mdast`](https://lexical.dev/docs/api/modules/lexical_mdast) — **Unified Markdown configuration and inherited export rules.** `MdastExtension` owns both import and export, and feature extensions include it automatically. Export rules accept a node class or type string and apply to subclasses, with more specific rules taking precedence. A `TextNode` rule can therefore handle tabs and custom text subclasses without separate registrations ([#9198](https://github.com/facebook/lexical/pull/9198))
*   [`@lexical/mdast`](https://lexical.dev/docs/api/modules/lexical_mdast) — **Import/export middleware through `context.next()`.** Delegate conversion of the same node to the remaining handlers, then return or modify their output. Export chains try remaining same-type rules before ancestor rules. Use `context.next()` to delegate: `null` retains its existing meaning of default export or omitted import, while `[]` omits content in either direction. Formatted text from middleware participates in text merging; custom structure or metadata is preserved as-is ([#9198](https://github.com/facebook/lexical/pull/9198))
*   [`@lexical/headless`](https://lexical.dev/docs/api/modules/lexical_headless) — **`HeadlessExtension`** works with `buildEditorFromExtensions` for server-side processing and tests. It marks the editor as headless and prevents attaching a DOM root. Unlike `createHeadlessEditor`, it keeps listener registration methods available, so extensions that register root listeners can still be used ([#9249](https://github.com/facebook/lexical/pull/9249))

### Notable Fixes

**Selection & editing**

*   Backspace over all text in the first block preserves the empty block and its type ([#9236](https://github.com/facebook/lexical/pull/9236)); select-all deletion restores an editable paragraph when the root still contains slots such as footnotes ([#9240](https://github.com/facebook/lexical/pull/9240)); line deletion correctly resolves endpoints inside text-bearing inline decorators ([#9247](https://github.com/facebook/lexical/pull/9247))
*   Dropping dragged text on the edge of its own selection no longer deletes it ([#9241](https://github.com/facebook/lexical/pull/9241)); persistent selection highlights retain coverage across mixed font sizes and partially overlapping rectangles ([#9196](https://github.com/facebook/lexical/pull/9196))
*   Lexical-controlled caret moves reveal the caret horizontally in code blocks and other scrolling containers, including table wrappers. Horizontal scrolling respects `scroll-padding`, with updated playground styling to keep the caret clear of the code gutter ([#9215](https://github.com/facebook/lexical/pull/9215))
*   Focus commands triggered during DOM selection synchronization no longer produce spurious read-only warnings; dispatching commands from an explicit read context still warns ([#9230](https://github.com/facebook/lexical/pull/9230))

**Mobile input & recovery**

*   iOS tracks explicit Shift key events so Shift+Enter can insert a line break without treating automatic capitalization as Shift. Ordinary Enter continues to insert paragraphs; when capitalization has already highlighted Shift, it must be toggled off and on to request a line break ([#9242](https://github.com/facebook/lexical/pull/9242))
*   The input-suppression workaround after handled Backspace or select-all is limited to macOS Chromium, preventing legitimate text input from being discarded on other platforms ([#9251](https://github.com/facebook/lexical/pull/9251))
*   `setEditorState` with a childless root warns and recovers to an empty paragraph in production. Development builds still report the invalid state through `onError` ([#9183](https://github.com/facebook/lexical/pull/9183))

**Performance & signals**

*   Writable nodes are cached during pending updates, reducing repeated node-map lookups ([#9169](https://github.com/facebook/lexical/pull/9169))
*   `@lexical/extension` raises its `@preact/signals-core` dependency to `^1.14.4`, fixing stale computed values after a batch changes a signal and restores its original value ([#9248](https://github.com/facebook/lexical/pull/9248))

**Playground & docs**

*   Getting-started guides now use module-level root extensions, with five live editors, examples drawn directly from runnable source, and Open in StackBlitz buttons. Custom-feature examples demonstrate `$config` schemas, NodeState, and HTML import/export through extensions ([#9237](https://github.com/facebook/lexical/pull/9237))
*   The introduction, Concepts and Serialization guides have been rewritten around current APIs, with Mermaid diagrams, an interactive update lifecycle explanation and a dedicated comparison with ProseMirror. Older setup and serialization APIs remain documented in Legacy sections ([#9249](https://github.com/facebook/lexical/pull/9249))
*   Numerous API names, snippets and lifecycle descriptions have been corrected throughout the docs, most of them by @potatowagon. API reference links now point to the right GitHub source paths and resolve external types ([#9204](https://github.com/facebook/lexical/pull/9204), [#9220](https://github.com/facebook/lexical/pull/9220))
*   Playground Find & Replace calculates offsets correctly across decorators and slots ([#9186](https://github.com/facebook/lexical/pull/9186))

## What's Changed
* v0.51.0 by @etrepum in https://github.com/facebook/lexical/pull/9178
* [lexical] Refactor: Cache writable nodes during pending updates by @etrepum in https://github.com/facebook/lexical/pull/9169
* [lexical-website] Documentation Update: fix $insertList argument in the commands guide by @potatowagon in https://github.com/facebook/lexical/pull/9188
* [lexical-website] Documentation Update: seed the editor state with setEditorState, not registerRichText by @potatowagon in https://github.com/facebook/lexical/pull/9189
* [lexical-website] Documentation Update: title the history page after its public API, not useHistory by @potatowagon in https://github.com/facebook/lexical/pull/9190
* [lexical-website] Documentation Update: use the real plugin component names in the editor-state React example by @potatowagon in https://github.com/facebook/lexical/pull/9191
* [lexical-website] Documentation Update: getElementByKey returns a DOM element, not a node by @potatowagon in https://github.com/facebook/lexical/pull/9192
* [lexical] Bug Fix: recover from an empty editor state instead of throwing by @potatowagon in https://github.com/facebook/lexical/pull/9183
* [lexical-playground] Bug Fix: Fix Find & Replace offsets across decorators and slots by @Monier-Ayman in https://github.com/facebook/lexical/pull/9186
* [lexical-website] Documentation Update: the deserialization hook is importJSON, not importFromJSON by @potatowagon in https://github.com/facebook/lexical/pull/9199
* [lexical-website] Documentation Update: useReactDecorators, and the three missing TextNode formats by @potatowagon in https://github.com/facebook/lexical/pull/9201
* [lexical-website] Documentation Update: clone is synthesized for $config nodes, and fix a setSomeData typo by @potatowagon in https://github.com/facebook/lexical/pull/9202
* [lexical-website] Documentation Update: node-replacement config uses nodes:, not nodes= by @potatowagon in https://github.com/facebook/lexical/pull/9203
* [lexical-website] Bug Fix: Include packages/ in API docs GitHub source links by @etrepum in https://github.com/facebook/lexical/pull/9204
* [lexical-website] Documentation Update: fix syntax errors, outdated APIs, and broken links in Node Transforms guide by @jaideepkrishna2008-ui in https://github.com/facebook/lexical/pull/9197
* [lexical-code-core][lexical-code-prism][lexical-code-shiki] Refactor: move the duplicated updateCodeGutter into @lexical/code-core by @Om-singhaI in https://github.com/facebook/lexical/pull/9164
* [lexical-mdast] Feature: Unify MdastExtension and inherit export rules by @etrepum in https://github.com/facebook/lexical/pull/9198
* [lexical-website] Documentation Update: fix two nodes.mdx snippets that do not compile by @potatowagon in https://github.com/facebook/lexical/pull/9200
* [lexical-website] Documentation Update: show $initialEditorState, not a function passed to createEditor by @potatowagon in https://github.com/facebook/lexical/pull/9205
* [lexical-website] Documentation Update: correct the shadow-DOM support table and two API references by @potatowagon in https://github.com/facebook/lexical/pull/9206
* [lexical-website] Documentation Update: fix the test path in the traversals maintenance comment by @potatowagon in https://github.com/facebook/lexical/pull/9207
* [lexical-website] Documentation Update: correct four claims in the extension lifecycle guide by @potatowagon in https://github.com/facebook/lexical/pull/9208
* [lexical-website] Documentation Update: re-sync the TabIndentationExtension snippet with its source by @potatowagon in https://github.com/facebook/lexical/pull/9209
* [lexical-website] Documentation Update: fix the broken snippets in the extensions migration guide by @potatowagon in https://github.com/facebook/lexical/pull/9211
* [lexical-website] Documentation Update: call the system Lexical Extensions, not Lexical Builder by @potatowagon in https://github.com/facebook/lexical/pull/9212
* [lexical-website] Documentation Update: fix two extension names that do not exist by @potatowagon in https://github.com/facebook/lexical/pull/9210
* [lexical-website] Documentation Update: add HMRExtension and KeyboardShortcutsExtension to the included list by @potatowagon in https://github.com/facebook/lexical/pull/9213
* [lexical-website] Documentation Update: fix seven incorrect API references in the DOM import guide by @potatowagon in https://github.com/facebook/lexical/pull/9222
* [lexical-website] Documentation Update: correct DOMExportOutput, DOMConversion priority, and the HeadingNode examples by @potatowagon in https://github.com/facebook/lexical/pull/9223
* [lexical-website] Documentation Update: fix the collaboration snippets and two stale playground references by @potatowagon in https://github.com/facebook/lexical/pull/9225
* [lexical-website] Documentation Update: correct five stale facts in the maintainers guide by @potatowagon in https://github.com/facebook/lexical/pull/9226
* [lexical-website] Documentation Update: setEditorState warns and recovers in production, it does not always throw by @potatowagon in https://github.com/facebook/lexical/pull/9228
* [lexical-website] Documentation Update: point the selection guide at NodeCaret by @potatowagon in https://github.com/facebook/lexical/pull/9229
* [lexical-website] Bug Fix: resolve external API links and shorten submodule labels by @etrepum in https://github.com/facebook/lexical/pull/9220
* [lexical] Bug Fix: Avoid read-only warnings for commit-time focus commands by @etrepum in https://github.com/facebook/lexical/pull/9230
* [lexical][lexical-html] Breaking Change: honor $createDOM overrides in default DOM export by @etrepum in https://github.com/facebook/lexical/pull/9231
* [lexical-website] Documentation Update: document the full update-listener payload and the cascade guard by @potatowagon in https://github.com/facebook/lexical/pull/9227
* [lexical-compiler] Bug Fix: Normalize temporary paths in watcher tests by @minwookshin in https://github.com/facebook/lexical/pull/9195
* [lexical][lexical-table][lexical-playground][lexical-website] Breaking Change: Standardize selection notification timing by @etrepum in https://github.com/facebook/lexical/pull/9219
* [benchmarks] Chore: Migrate benchmarks to vitest 5 and add type checking in CI by @etrepum in https://github.com/facebook/lexical/pull/9235
* [lexical][lexical-code-core][lexical-playground] Bug Fix: reveal the caret in code blocks and other containers that scroll sideways by @Om-singhaI in https://github.com/facebook/lexical/pull/9215
* [lexical] Bug Fix: Preserve first block after backward range deletion by @Abdul-Azhar-Jamesh in https://github.com/facebook/lexical/pull/9236
* [lexical-selection][lexical-utils] Bug Fix: Preserve selection coverage across mixed typography by @minwookshin in https://github.com/facebook/lexical/pull/9196
* [lexical-clipboard] Bug Fix: Keep dragged text when it is dropped on the edge of its own selection by @kwy404 in https://github.com/facebook/lexical/pull/9241
* [lexical] Breaking change: Remove LexicalNode.getCommonAncestor by @mayrang in https://github.com/facebook/lexical/pull/9238
* [lexical] Bug Fix: Handle select-all deletion with root slots by @etrepum in https://github.com/facebook/lexical/pull/9240
* [lexical-website] Documentation Update: Document ErrorBoundary configuration for PlainTextExtension by @dvd233 in https://github.com/facebook/lexical/pull/9182
* [lexical] Bug Fix: resolve line deletion endpoints inside inline decorators by @maximilliangrand in https://github.com/facebook/lexical/pull/9247
* [lexical] Bug Fix: Limit handled selection input suppression to macOS Chrome by @etrepum in https://github.com/facebook/lexical/pull/9251
* [lexical-playground] Chore: Harden nested editor selection ownership test by @etrepum in https://github.com/facebook/lexical/pull/9243
* [lexical-extension] Chore: Update signals-core to 1.14.4 by @etrepum in https://github.com/facebook/lexical/pull/9248
* [lexical-website][examples] Documentation: Modernize the getting-started section by @etrepum in https://github.com/facebook/lexical/pull/9237
* [lexical] Bug Fix: Track explicit Shift state for iOS line breaks by @etrepum in https://github.com/facebook/lexical/pull/9242
* [lexical][lexical-headless][lexical-website] Documentation Update: Rewrite the introduction and modernize the Concepts, Serialization docs and examples by @etrepum in https://github.com/facebook/lexical/pull/9249

## New Contributors
* @Monier-Ayman made their first contribution in https://github.com/facebook/lexical/pull/9186
* @jaideepkrishna2008-ui made their first contribution in https://github.com/facebook/lexical/pull/9197
* @minwookshin made their first contribution in https://github.com/facebook/lexical/pull/9195
* @Abdul-Azhar-Jamesh made their first contribution in https://github.com/facebook/lexical/pull/9236
* @kwy404 made their first contribution in https://github.com/facebook/lexical/pull/9241
* @dvd233 made their first contribution in https://github.com/facebook/lexical/pull/9182
* @maximilliangrand made their first contribution in https://github.com/facebook/lexical/pull/9247

**Full Changelog**: https://github.com/facebook/lexical/compare/v0.51.0...v0.52.0

Related Repositories