Back to Plate

Beforeinput substitutions must flush native text before replacement

docs/solutions/logic-errors/2026-05-09-beforeinput-substitutions-must-flush-native-text-before-replacement.md

53.0.62.7 KB
Original Source

Beforeinput substitutions must flush native text before replacement

Problem

Browser text substitutions can send a native insertText beforeinput, mutate the DOM, then send a model-owned replacement before the matching input event imports the native text.

If Slate applies the replacement first, the native DOM text is lost or the replacement lands at the wrong current selection.

Symptoms

  • Autocapitalization flow i + native S + replacement I rendered I instead of IS.
  • Flushing native text without preserving the replacement target rendered iSI.
  • Double-space period replacement after 🙂 rendered 🙂 . instead of 🙂. .

Solution

The runtime now treats native text beforeinput as pending model work until the matching input event or the next model-owned beforeinput:

  • runtime-before-input-events.ts queues native text repair when Slate allows native insertText.
  • The next beforeinput flushes queued native repair before applying the model-owned replacement.
  • mutation-controller.ts applies provided replacement target ranges directly instead of relying on the current selection.
  • selection-reconciler.ts honors expanded insertText target ranges even when model selection is preferred.

Why This Works

Mac autocorrect and punctuation substitution are ordered around browser-owned DOM text. Slate must import the browser-owned text before applying the later model-owned replacement, but the replacement still has to use the event's target range rather than whatever selection the repair leaves behind.

Expanded insertText target ranges are replacement instructions. Collapsed insertText ranges can still prefer the current model selection.

Prevention

  • Browser beforeinput substitution tests should dispatch getTargetRanges() and include both native DOM mutation and follow-up model-owned replacement.
  • Unit tests should lock the two smaller owners: expanded insertText target range import, and replacement text using the provided selection.
  • Do not convert OS labels, emoji product rendering, theme spans, or raw mobile claims into generic Slate behavior unless a separate owner accepts them.