Skip to content

[Breaking Change][lexical-extension][lexical-rich-text][lexical-plain-text] Feature: Remove empty inline elements#8497

Merged
etrepum merged 4 commits into
facebook:mainfrom
levensta:remove-empty-inline
May 13, 2026
Merged

[Breaking Change][lexical-extension][lexical-rich-text][lexical-plain-text] Feature: Remove empty inline elements#8497
etrepum merged 4 commits into
facebook:mainfrom
levensta:remove-empty-inline

Conversation

@levensta

@levensta levensta commented May 11, 2026

Copy link
Copy Markdown
Contributor

Description

Breaking Change

A new extension, NormalizeInlineElementsExtension, has been added. It removes any empty inline ElementNodes. This extension is enabled by default for RichTextExtension and PlainTextExtension. For custom inline elements that are explicitly declared as emptyable and return true from canBeEmpty, a warning will be logged to the console

If you need to preserve empty inline elements, you can temporarily disable the extension during the migration:

const appExtension = defineExtension({
  dependencies: [
    RichTextExtension,
    configExtension(NormalizeInlineElementsExtension, {disabled: true}),
  ],
  name: 'MyEditor',
  namespace: 'MyEditor',
});

If you think there are useful cases where empty inline elements should be preserved, please open an issue with a detailed description and an example

Closes #8205

Test plan

Before

If a user creates an emptyable inline element node, this creates “invisible” positions in the document that cannot be navigated

After

Inline nodes with isInline(): true and isEmpty(): true are removed at runtime, and a warning is logged to the console

@vercel

vercel Bot commented May 11, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview, Comment May 12, 2026 8:40pm
lexical-playground Ready Ready Preview, Comment May 12, 2026 8:40pm

Request Review

@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 May 11, 2026
Comment thread packages/lexical/src/LexicalUpdates.ts Outdated

@etrepum etrepum left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I wonder if it would be better to just register a transform on ElementNode instead of adding more code directly to the update cycle

Comment thread packages/lexical/src/LexicalUpdates.ts Outdated
@etrepum

etrepum commented May 11, 2026

Copy link
Copy Markdown
Collaborator

This should probably also be marked as a breaking change. Even though they were basically broken before you could still create empty inline ElementNodes before that had some behavior. Perhaps the right solution is to add transforms in RichTextExtension and PlainTextExtension that can be configured on or off (default on). This way anyone with legacy code that predates extensions would work as-is and people using a newer stack can still opt-out of the transform while they migrate their code.

@etrepum etrepum added the extended-tests Run extended e2e tests on a PR label May 11, 2026
@levensta

levensta commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

I wonder if it would be better to just register a transform on ElementNode instead of adding more code directly to the update cycle

This cannot be done, at the very least because ElementNode does not implement the getType method and is not explicitly registered in the editor, which will cause an error. Additionally, registerNodeTransform handlers do not propagate to subclasses, unlike the $transform property in config(), but I'm not sure if ElementNode can be rewrite to config() without causing regression

@etrepum

etrepum commented May 11, 2026

Copy link
Copy Markdown
Collaborator

There are plenty of ways to do it like just manipulating editor._nodes. We don't have to only rely on public APIs for internal functionality, e.g.

if (!config.disableEmptyInlineElementNormalization) {
  for (const {klass, transforms} of editor._nodes.values()) {
    if (klass.prototype instanceof ElementNode && klass.prototype.isInline !== ElementNode.prototype.isInline) {
      transforms.add(normalizeEmptyInlineElementTransform);
    }
  }
}

@levensta levensta force-pushed the remove-empty-inline branch from d41ae1b to 04a7176 Compare May 11, 2026 22:13
@levensta levensta changed the title [lexical] Feature: Remove emptyable inline elements and print warning before transforms [lexical-rich-text][lexical-plain-text] Breaking Change: Remove empty inline elements May 11, 2026
@levensta

Copy link
Copy Markdown
Contributor Author

I set the default option to false for register functions and true for the extensions API. This way, the change won’t break users of legacy plugins, while extensions will immediately behave correctly, with the option to revert the change

Comment thread packages/lexical-plain-text/src/index.ts Outdated
Comment thread packages/lexical-plain-text/src/index.ts Outdated
Comment thread packages/lexical-plain-text/src/index.ts Outdated
@levensta levensta changed the title [lexical-rich-text][lexical-plain-text] Breaking Change: Remove empty inline elements [lexical-extension][lexical-rich-text][lexical-plain-text] Breaking Change: Remove empty inline elements May 12, 2026
@etrepum etrepum changed the title [lexical-extension][lexical-rich-text][lexical-plain-text] Breaking Change: Remove empty inline elements [Breaking Change][lexical-extension][lexical-rich-text][lexical-plain-text] Feature: Remove empty inline elements May 12, 2026
@etrepum etrepum added this pull request to the merge queue May 13, 2026
Merged via the queue into facebook:main with commit 68f1638 May 13, 2026
42 checks passed
@etrepum etrepum mentioned this pull request May 28, 2026
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. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Empty inline ElementNode is not deleted inside an other ElementNode

2 participants