Skip to content

fix: roundtrip empty Lexical nodes on mode switch#3112

Open
Soxasora wants to merge 2 commits into
stackernews:masterfrom
Soxasora:fix/editor-empty-nodes-roundtrip
Open

fix: roundtrip empty Lexical nodes on mode switch#3112
Soxasora wants to merge 2 commits into
stackernews:masterfrom
Soxasora:fix/editor-empty-nodes-roundtrip

Conversation

@Soxasora

@Soxasora Soxasora commented Jun 18, 2026

Copy link
Copy Markdown
Member

Description

Fixes #3109 by not detecting empty nodes when switching editor modes.

Mode switches are based on the current Formik value. Triggered on each mode switch and submit, syncFormik checks if the Lexical editor state contains nodes but no text content. If so, it resets the text field, helping Formik validation catch empty item creation.

This should happen only on submission, not on mode switches. This PR makes mode switches call syncFormik with the flag switchMode: true, avoiding cleanup.

Screenshots

empty-tables-roundtrip.mp4

Additional Context

This issue exacerbated another, bigger, issue: empty items can be created via markdown mode!
markdown mode always contains text content, syncFormik and $isTextEmpty() can't catch empty markdown nodes.
I'll open a separate issue for this.

Checklist

Are your changes backward compatible? Please answer below:

Yes

On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:

7, empty content roundtrips on mode switches, submission from compose still doesn't allow empty nodes

For frontend changes: Tested on mobile, light and dark mode? Please answer below:
n/a

Did you introduce any new environment variables? If so, call them out explicitly here:
n/a

Did you use AI for this? If so, how much did it assist you?
No


Note

Low Risk
Localized editor/Formik sync and markdown export behavior; submit-time empty validation is unchanged when switchMode is false.

Overview
Fixes mode-switch roundtripping where compose content with empty structural nodes (e.g. blank paragraphs) was wiped because syncFormik treated “no text” like submit-time validation and cleared Formik’s text.

syncFormik and SYNC_FORMIK_COMMAND now take an options object (switchMode, flush). The empty-text → '' shortcut is skipped when switchMode is true. The toolbar dispatches sync with { switchMode: true } before toggling modes.

Code blocks: Lexical → mdast export no longer forces lang: null for text or missing language—it writes the Lexical language value through for better roundtrip fidelity.

Reviewed by Cursor Bugbot for commit fdc431e. Bugbot is set up for automated code reviews on this repo. Configure here.

@sir-opti

sir-opti commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Tested to work with ul, ol, tables, code blocks.

Odd behaviors spotted:

text markdown type gets stripped

(not on post from write, so I suspect that it gets stripped on switch to compose?)

```text
```

turns into

```
```

checkboxes go wild

- [ ]
- [ ]
- [ ] non-empty
- [ ]
- [ ]

turns into

* \[ ]
* \[ ]

- [ ] non-empty

* \[ ]
* \[ ]

but on current master it turns into something even more confusing:

* [ ] \[ ]
* [ ] \[ ]
* [ ] non-empty
* [ ] \[ ]
* [ ] \[ ]

Doesn't have to be blocking for this PR - can track it separately

@Soxasora

Soxasora commented Jun 19, 2026

Copy link
Copy Markdown
Member Author

text markdown type gets stripped

Forgot to change this in the MDAST code block visitor! Before #2795, Lexical Shiki didn't support a null default language so we just treated text as the de-facto null language. Now that it's supported, I've removed this weird roundtrip hack.

checkboxes go wild

Per GFM spec, checklist items that don't have trailing text are not checklist items. The behavior in prod is wrong, and got fixed by #3105.
Technically we can diverge from the spec whenever we want, and can fix this on the import side (markdown -> lexical) by forcing blank checklist items. But we cannot (comfortably) fix this on the export side (lexical -> markdown) because GFM injects checkboxes via a regex that needs whitespace after the bullet, fixing this would require overriding the listItem toMarkdown handler and lots of headaches.

imo, I prefer the behavior that we have now post-3105, it's correct and spec-compliant:

  • [ ]
  • [ ]
  • non-empty
  • [ ]
  • [ ]

Thank you opti!!

@sir-opti

Copy link
Copy Markdown
Contributor

I prefer the behavior that we have now post-3105

Agreed. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

text of only empty table gets deleted on lexical->markdown transform

2 participants