npm @lexical/react 0.51.0
v0.51.0

latest release: 0.51.1-nightly.20260918.0
7 hours ago

v0.51.0 is a monthly release with one large new feature: nodes can now declare their JSON serialization as a schema on $config. This declarative schema is faster, safer, and more flexible than the hand-written methods. The metadata it provides enables a compact export option with all default values elided, the code generator that provides faster import and export for every node that lexical ships, a new @lexical/fast-check package for property-based tests, and it provides a path to future features that rely on knowing the structure of the nodes (e.g. additional codecs and dev tools).

This is a big step towards Lexical 1.0, the next few releases are expected to be focused on moving everything (including docs and examples) to the latest Extension and $config APIs and removing deprecated functionality. Two related changes in this release are that we are publishing ESM-only to npm now (cjs builds are still supported in the source tree), and LexicalComposer is deprecated for LexicalExtensionComposer. Expect anything @deprecated to be removed soon.

Breaking Changes

  • npm packages are ESM only. Every package declares "type": "module" and its exports map offers development / production / default conditions with no require branch; the CommonJS build remains in-tree only for Meta's www. Bundlers are unaffected. CommonJS code can require() the packages on Node.js 20.19+ (the builds have no top-level await), and older Node.js must use await import(...) — but a dependency the app also loads through require() can then exist twice, which breaks handing its objects to Lexical (yjs docs to @lexical/yjs, @preact/signals-core signals from @lexical/extension), so load those as ESM too. Metro bundles both builds unless you add the condition for Lexical's packages; see the module formats FAQ (#9127)
  • lexicalexportJSON may serialize the instance as-is. A property declared with withField is read straight off the node instead of through its accessor, so exportJSON no longer resolves getLatest() for you and a stale reference can write stale values (and a property whose accessor a subclass overrode still resolves, so one object can mix the two). Nothing inside Lexical is affected — the walk, the clipboard selection export and editorState.toJSON() all start from the node map — but call getLatest() yourself on a reference you kept across a mutation (#8602)
  • @lexical/code — the deprecated Prism re-exports are gone, along with the @lexical/code-prism dependency (so Prism no longer lands in a bundle that only imports @lexical/code). Import CODE_LANGUAGE_FRIENDLY_NAME_MAP, CODE_LANGUAGE_MAP, getCodeLanguageOptions, getCodeLanguages, getCodeThemeOptions, getLanguageFriendlyName, normalizeCodeLanguage (formerly also normalizeCodeLang), PrismTokenizer and registerCodeHighlighting from @lexical/code-prism (#9141)

Deprecations

  • @lexical/reactLexicalComposer is deprecated in favor of LexicalExtensionComposer and is expected to be removed in a future major. LexicalComposer cannot accept extensions, so any feature shipped as one is unreachable from an editor it builds; the migration is usually a two-line change (#9168, React extensions guide)

New APIs & Features

  • lexicalDeclarative serialization schemas (experimental). A node that uses $config declares its serialized properties once in the new json property, and that single declaration drives importJSON, updateFromJSON and exportJSON: nodeSchema, withField / withAccessors, and the value combinators (stringValue, numberValue, booleanValue, enumValue, arrayValue, objectValue, unionValue, rawValue, transformValue, nullable, optional, …). Every built-in node declares one, and most custom nodes now need no JSON serialization code at all. Output is unchanged by default (#8602, serialization docs).
  • lexicalCompact JSON export, opt-in via editorState.toJSON(true) or $withCompactExport(true, cb) (with $isCompactExport() for schema getters, plus $exportNodeJSON and the CompactSerializedEditorState / ParsableSerializedEditorState / LexicalParseJSON / SerializedPartial types). It omits properties equal to their schema default, properties the parser derives, and the deprecated version. The raw objects are well under half the legacy size — which matters for structured clones and worker messages — but after gzip the two are a wash. Both forms parse back to the same document; only a Lexical new enough to know the schemas can read the compact one, so keep writing the legacy form until every reader is upgraded (#8602)
  • @lexical/compilerGenerated serialization code. Every built-in node class ships straight-line serialization generated from its own schema at build time, producing byte-identical JSON to the schema-driven path that continues to serve custom nodes (#8602)
  • @lexical/fast-check — New package deriving fast-check arbitraries from those schemas: nodeArbitrary(NodeClass) generates exactly the serialized JSON a node's parser accepts, each property independently present or absent. Useful for the bugs fixtures hide, such as a property afterCloneFrom forgot to copy. fast-check is a peer dependency; getComposedSchemaFields in core inspects a class's schema without generating anything (#8602)
  • @lexical/extension — Published as subpath exports, one entry per extension (@lexical/extension/AutoFocusExtension, …), with compiler and lint enforcement of the source import convention. The barrel import is unchanged, and shared modules such as signals keep a single instance (#9161). This is primarily only useful as a consumer if you are not using tree-shaking.

Notable Fixes

Bundling, tree-shaking & types

  • A production import of only createCommand no longer retains the editor (#9121), and the remaining module-scope side effects that defeated tree-shaking are gone, with a bare-import residue check now run over every entry (#9124); extension editors build under loose-mode transpilation (#9134)
  • Flow: defineExtension was uncallable, making the extension types unusable (#9132); declarations can now express an extension with dependencies (#9176); modern variance keywords survive into the www stubs (#9154)

Selection & editing

  • No redundant selection-change command on Safari link selection (#9163); TextNode.setMode, setTextContent and isSimpleText read the latest state (#9162); $removeTextFromCaretRange no longer leaves a childless list or table as the root's only child (#9116); a text entity keeps the style and detail of the text it replaces (#9130); selection overlay listeners are notified when rectangles are removed (#9117)

Lists, tables & Markdown

  • Indenting between two nested lists no longer converts the second list's type (#9118); $mergeCells no longer keeps the empty paragraph of the target cell (#9122); text left outside a Markdown link keeps its format (#9131)

Playground & docs

  • Card/Review CSS placeholders are cleared reliably on paste in Safari (#9129) and releasing a CardNode drag no longer selects the node (#9135); new docs on Lexical being a singleton per app, and why a library must declare it as a peer dependency (#9125)

Plus the usual dependency updates across the rollup, shikijs, vitest, docusaurus/typedoc and flow/hermes groups.

What's Changed

  • v0.50.0 by @etrepum in #9110
  • [lexical][lexical-compiler] Bug Fix: Let esbuild drop the editor from a createCommand-only production import by @zurfyx in #9121
  • [*] Bug Fix: Remove the remaining module-scope side effects that defeat tree-shaking by @etrepum in #9124
  • [lexical-playground] Bug Fix: clear Card/Review CSS placeholders reliably on paste in Safari by @sahiee-dev in #9129
  • [lexical-website] Documentation Update: document that Lexical is a singleton per app and libraries must declare it as a peer dependency by @potatowagon in #9125
  • [lexical-markdown] Bug Fix: text left outside a markdown link keeps its format by @Om-singhaI in #9131
  • [lexical] Fix: make the extension Flow types usable (defineExtension was uncallable) by @potatowagon in #9132
  • build(deps-dev): bump esbuild from 0.27.7 to 0.28.1 by @dependabot[bot] in #9123
  • [lexical-table] Bug Fix: $mergeCells no longer keeps the empty paragraph of the target cell by @Om-singhaI in #9122
  • [lexical-utils] Bug Fix: notify when selection overlay rectangles are removed by @mehuljariwala in #9117
  • [lexical] Bug Fix: $removeTextFromCaretRange no longer leaves a childless list or table as the root's only child by @Om-singhaI in #9116
  • [lexical-playground] Bug Fix: avoid node selection on CardNode drag release by @kavyansh18 in #9135
  • [lexical-extension] Fix: use Array.from over iterable spread so extension editors build under loose-mode transpilation by @potatowagon in #9134
  • [lexical-text] Bug Fix: a text entity keeps the style and detail of the text it replaces by @Om-singhaI in #9130
  • [*] Breaking change: Publish ESM only to npm and keep the CommonJS build for www by @etrepum in #9127
  • [lexical-code] Breaking change: drop the deprecated prism re-exports and the @lexical/code-prism dependency by @etrepum in #9141
  • build(deps-dev): bump vitest from 4.1.8 to 4.1.11 by @dependabot[bot] in #9140
  • build(deps): bump the docusaurus-and-typedoc group with 13 updates by @dependabot[bot] in #9142
  • build(deps-dev): bump the flow-and-hermes group with 4 updates by @dependabot[bot] in #9143
  • build(deps-dev): bump baseline-browser-mapping from 2.10.33 to 2.11.0 by @dependabot[bot] in #9139
  • build(deps): bump the shikijs group with 6 updates by @dependabot[bot] in #9145
  • build(deps): bump the rollup group across 1 directory with 5 updates by @dependabot[bot] in #9144
  • [lexical-list] Bug Fix: indenting between two nested lists no longer converts the second list's type by @Om-singhaI in #9118
  • [*] Bug Fix: Keep modern Flow variance keywords in the www Flow stubs by @potatowagon in #9154
  • build(deps-dev): bump the dev-dependencies group across 1 directory with 26 updates by @dependabot[bot] in #9150
  • build(deps-dev): bump flow-bin from 0.330.0 to 0.331.0 in the flow-and-hermes group by @dependabot[bot] in #9156
  • [lexical-extension][*] Refactor: publish @lexical/extension as subpath exports with compiler+lint enforcement by @etrepum in #9161
  • [lexical] Bug Fix: Avoid redundant selection change command by @levensta in #9163
  • [lexical] Bug Fix: TextNode setMode, setTextContent and isSimpleText read the latest state by @Dev-next-gen in #9162
  • [lexical-react] Chore: Deprecate LexicalComposer in favor of LexicalExtensionComposer by @potatowagon in #9168
  • build(deps-dev): bump eslint from 10.9.1 to 10.10.0 in the dev-dependencies group across 1 directory by @dependabot[bot] in #9158
  • build(deps): bump date-fns from 4.1.0 to 4.4.0 by @dependabot[bot] in #9159
  • [lexical][*] Feature: node JSON serialization schemas, with compact export by @etrepum in #8602
  • build(deps): bump @babel/parser from 8.0.0 to 8.0.4 by @dependabot[bot] in #9160
  • build(deps): bump immutable from 4.3.8 to 4.3.9 by @dependabot[bot] in #9175
  • build(deps-dev): bump the vitest group across 1 directory with 2 updates by @dependabot[bot] in #9157
  • [lexical] Fix: Flow declarations cannot express an extension with dependencies by @potatowagon in #9176

New Contributors

Full Changelog: v0.50.0...v0.51.0

Don't miss a new react release

NewReleases is sending notifications on new releases.