Skip to content

[lexical] Bug Fix: Avoid computing document text when no text content listener is registered - #9528

Closed
Techsynx wants to merge 3 commits into
meta:mainfrom
Techsynx:fix/headless-commit-text-content
Closed

Techsynx wants to merge 3 commits into
meta:mainfrom
Techsynx:fix/headless-commit-text-content

Conversation

@Techsynx

Copy link
Copy Markdown

Description

In $commitPendingUpdatesImpl, riggerTextContentListeners was invoked on every commit to compare previous and pending editor state text. In headless editors where RootNode.__cachedText is not populated by the DOM reconciler, getEditorStateTextContent() traverses the entire document tree twice per commit even when no text content listeners are registered (editor._listeners.textcontent.size === 0). This introduced an avoidable O(document) cost per update in headless environments (such as server-side execution, unit tests, scripts, and @lexical/y headless relay bindings).

This change adds an early return to riggerTextContentListeners when editor._listeners.textcontent.size === 0, ensuring document text is only computed when active text content listeners exist.

Closes #9518

Changeset

Added .changeset/skip-text-content-computation.md with:
[perf] Avoid computing full document text on commit when no text content listener is registered

Test plan

Before

Edits in an editor without a registered text content listener invoked RootNode.prototype.getTextContent twice per commit.

After

  • With no text content listener registered, RootNode.prototype.getTextContent is called 0 times during commits.
  • When a text content listener is registered via
    egisterTextContentListener, document text is computed and dispatched as expected.
  • Added unit tests in packages/lexical/src/tests/unit/LexicalEditorListener.test.ts validating both behaviors.
  • Verified test suite and TypeScript checks: pnpm exec vitest run --project unit packages/lexical/src/tests/unit/LexicalEditorListener.test.ts and pnpm run tsc-test pass cleanly.

@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
lexical Ready Ready Preview Nov 2, 2026 9:03am UTC
lexical-playground Ready Ready Preview Nov 2, 2026 9:03am UTC

Request Review

@Techsynx
Techsynx requested a review from etrepum as a code owner October 29, 2026 10:00
@meta-cla

meta-cla Bot commented Oct 29, 2026

Copy link
Copy Markdown

Hi @Techsynx!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at [email protected]. Thanks!

@Techsynx

Copy link
Copy Markdown
Author

CLA has been signed. Thank you!

@meta-cla

meta-cla Bot commented Oct 29, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 29, 2026

@meta-code-review meta-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking up #9518 and for the clear write-up. The same fix has since landed on main: #9548 (merged 2026-10-30) adds the identical early return to triggerTextContentListeners, and #9518 is closed. After your latest commit, this branch's packages/lexical/src/LexicalUpdates.ts is byte-for-byte the file on main, so merging would change only two things:

  • .changeset/skip-text-content-computation.md records the same lexical [perf] change as .changeset/lexical-text-content-without-listeners.md, which is already on main. CONTRIBUTING.md ("Changesets") explains that each changeset becomes that change's entry in CHANGELOG.md, so this one would list the change a second time.
  • The two tests in LexicalEditorListener.test.ts overlap packages/lexical/src/__tests__/unit/HeadlessTextContent.test.ts, which #9548 added. It already checks that headless commits compute no root text while nothing listens, that a listener still gets each text change (and not a format-only change), and that nothing is computed after it unregisters.

So I'd suggest closing this pull request as superseded by #9548. If you think a case is missing from HeadlessTextContent.test.ts, it could go in as a small test-only Chore pull request with no changeset (AGENTS.md, "Commits and Pull Requests").

Two tips for next time:

  • To bring a branch up to date, merge or rebase onto main instead of copying files over it. The copy is why the diff here also shows the update-error and INTERNAL_getActiveEditorState code from other pull requests.
  • AGENTS.md asks for the actual Before/After command output to be pasted into the Test plan, not just described.

'lexical': patch
---

[perf] Avoid computing full document text on commit when no text content listener is registered

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This records the same lexical [perf] change as .changeset/lexical-text-content-without-listeners.md, which #9548 already added to main. Per CONTRIBUTING.md ("Changesets"), each changeset becomes the change's CHANGELOG.md entry, so merging this would list the change twice.

@Techsynx

Techsynx commented Nov 2, 2026

Copy link
Copy Markdown
Author

Thanks for picking up #9518 and for the clear write-up. The same fix has since landed on main: #9548 (merged 2026-10-30) adds the identical early return to triggerTextContentListeners, and #9518 is closed. After your latest commit, this branch's packages/lexical/src/LexicalUpdates.ts is byte-for-byte the file on main, so merging would change only two things:

  • .changeset/skip-text-content-computation.md records the same lexical [perf] change as .changeset/lexical-text-content-without-listeners.md, which is already on main. CONTRIBUTING.md ("Changesets") explains that each changeset becomes that change's entry in CHANGELOG.md, so this one would list the change a second time.
  • The two tests in LexicalEditorListener.test.ts overlap packages/lexical/src/__tests__/unit/HeadlessTextContent.test.ts, which [lexical] Bug Fix: Compute the document's text on commit only when a text content listener is registered #9548 added. It already checks that headless commits compute no root text while nothing listens, that a listener still gets each text change (and not a format-only change), and that nothing is computed after it unregisters.

So I'd suggest closing this pull request as superseded by #9548. If you think a case is missing from HeadlessTextContent.test.ts, it could go in as a small test-only Chore pull request with no changeset (AGENTS.md, "Commits and Pull Requests").

Two tips for next time:

  • To bring a branch up to date, merge or rebase onto main instead of copying files over it. The copy is why the diff here also shows the update-error and INTERNAL_getActiveEditorState code from other pull requests.
  • AGENTS.md asks for the actual Before/After command output to be pasted into the Test plan, not just described.

Thanks for the thorough review and the helpful feedback! Glad to see #9548 resolved the root issue. Closing this PR as superseded. Appreciate the tips on rebasing and the test plan formatting for future contributions

@Techsynx Techsynx closed this Nov 2, 2026

This branch was successfully deployed

2 active deployments
Preview – lexical — 0bfbd168 Deployed Nov 2, 2026 by vercel[bot]
Preview – lexical-playground — 0bfbd168 Deployed Nov 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: [lexical] Every commit of a headless editor computes the whole document's text twice, even with no text content listener

1 participant