Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Lexical blocks: Save Draft submits stale fields while a child form-state response is pending (3.89.0)

Open
#18,907 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
45/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active

Research direction

Start with the five-file characterization test in the linked gist and trace the child Form's debounced getFormState call, BlockComponent's setFields update, RichText's deferred parent useField.setValue, and the native SaveDraftButton submit path. Reproduce the ordering gap, then investigate how pending child responses and the editor-to-Form update can be coordinated before the parent Form snapshots fields. Done means the native save includes both updated captions while block B's response is pending, with regression coverage for this ordering.

Written by the indexing model from the issue text.

Description

area: ui; plugin: richtext-lexical Bug
Describe the Bug

In Payload 3.89.0, the enabled native Save Draft button can submit an earlier Lexical block value while a later edit in another block is still waiting for its nested Form's form-state response.

A deterministic component-level reproduction mounts the public RichTextField and BlocksFeatureClient, real Lexical BlockNodes/BlockComponents, their nested Payload Forms and TextFields, a parent document Form, and the native SaveDraftButton. No editor, field, node, provider, or submit implementation is patched or replaced.

Block A is edited and allowed to reach the parent document Form first. The parent is genuinely modified, so Save Draft is enabled in update mode. Block B's actual input is then edited, but its local form-state transport response is held. Clicking the native button sends a draft PATCH containing A's updated caption and B's previous caption, without waiting for B's response.

After releasing that response and letting the native block/editor/idle propagation complete, another native Save Draft sends both updated captions.

Expected: saving should either coordinate pending child-block changes before taking the document snapshot, or keep the save action unavailable until those changes have reached the document Form.

Actual: an enabled Save Draft completes with the earlier document snapshot while the later child-block change is pending.

Link to the code that reproduces this issue

https://gist.github.com/Nijat-Hamid/b38b76255255bf31902d5425913c6231

The reproduction contains only synthetic captions and minimal test configuration. Save its five files together in a new empty directory, use Node.js 24.18.0 or later, and run npm install followed by npm test. It also passed after a separate fresh npm install --ignore-scripts --no-audit --no-fund; it does not rely on a pre-existing application's dependency installation.

Reproduction Steps
  1. Run the linked characterization test. It seeds two instances of a minimal Lexical block with one text caption field each.

  2. Edit block A's real input to First A caption edit. Complete its real child Form debounce, the local form-state response, BlockComponent node update, and outer RichText idle callback. Assert the parent is modified and holds A's updated value.

  3. Edit block B's distinct real input to Second B caption edit. Let the real child Form invoke the local form-state transport, but hold that response unresolved. The test also checks that A's input did not change.

  4. Click the actual enabled #action-save-draft button. Capture its native PATCH FormData and inspect _payload. The caption projection is:

    [First A caption edit, Original B caption]
    
  5. Release B's form-state response, then let the actual BlockComponent setFields update and outer RichText idle callback finish. Click native Save Draft again. Its next _payload contains:

    [First A caption edit, Second B caption edit]
    

The test intentionally passes when it observes this ordering gap; that is a characterization, not the desired fixed behavior. The child response is deferred to make the ordering deterministic, not to implement an application workaround.

Scope of the reproduction
  • The public UI/editor path and real input/button interactions run in jsdom. CSS is inert; normal-shaped browser observers and Next navigation context values are supplied by the harness.
  • Only server-function/fetch transport is synthetic: form-state returns the supplied field state, preference lookups return local 404s, and draft PATCHes return synthetic HTTP 200 responses. Real sockets and HTTP are blocked.
  • No database, CMS server, environment file, account, or real content is involved. This does not establish a production data-loss incident.
  • The complete hosted Edit view's onChange/onSave, real server normalization/validation, database persistence/reload, Autosave and Publish are not tested. No claim is made that those other save paths were reproduced or fixed.
Source trace in the pinned packages

The child Form's debounced onChange awaits getFormState; BlockComponent then schedules editor.update(() => node.setFields(...)). The outer RichText Field defers its parent useField.setValue through useRunDeprioritized (idle timeout 500ms). Native Save Draft invokes the parent Form.submit, which snapshots its current fields before its beforeSubmit callbacks.

I could not find a documented public barrier that waits for both pending child Form responses and the outer RichText-to-Form bridge before that snapshot. An editor-state read alone would not await the child Form's request. If there is a supported coordination hook I have missed, guidance would be appreciated.

Possibly adjacent: #18425, but this report concerns nested Lexical block values in the outgoing document save, rather than late condition/validation metadata merging.

Which area(s) are affected?

area: ui; plugin: richtext-lexical

Environment Info
Payload / @payloadcms/ui / @payloadcms/next / @payloadcms/richtext-lexical: 3.89.0
@payloadcms/translations: 3.89.0
Lexical (installed dependency): 0.41.0
Next.js: 16.3.5
React / React DOM: 19.3.0
Node.js: 24.18.0
Vitest: 5.0.0
jsdom: 30.0.1
Vite: 8.1.5

These are the tested versions, not a claim about the latest v3/v4 release. No package patch or workaround has been applied.

Dominant language
TypeScript
Stars
45.3k
Forks
4.3k
Avg merge
1d 19h
Merged PRs (30d)
299

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from payloadcms/payload

All issues in payloadcms/payload

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.