Skip to content

Sign-in experience: CJK IME composition intermittently cancelled in identifier input (controlled value rewritten during composition) #9637

Description

@Colinchiu007

Describe the bug

On the sign-in experience, when a CJK IME (e.g. Microsoft Pinyin) is active, typing letters into the identifier input (username / email / phone combined field, SmartInputField) intermittently fails to start IME composition: the candidate window does not appear and the underlined pre-edit text is cancelled. Typing the exact same keys again may work. This is random but strongly correlated with typing speed — fast typing almost always breaks composition, slow typing often works.

This is not Electron-specific: it reproduces identically in the latest desktop Chrome and Edge on Windows.

User-visible impact: CJK users who keep their IME in Chinese mode (the default state for most of them) cannot reliably type even a plain ASCII username. They are forced to manually switch to English mode first, which is a poor sign-in experience.

Expected behavior

IME composition should never be interrupted while typing. Characters that are not allowed in a username should be rejected by form validation after composition is committed (the validation error path already exists), not by breaking the composition session mid-flight.

How to reproduce

  1. Self-hosted Logto (Docker image svhd/logto:1.41.0) with sign-in identifier username enabled (the combined "username / email / phone" input is shown).
  2. On Windows, activate Microsoft Pinyin (Chinese) input mode.
  3. Open the sign-in page (via a normal OIDC authorize flow so an interaction exists) and focus the identifier input.
  4. Type a pinyin syllable quickly, e.g. z h e.
  5. Observed: often no candidate window appears and nothing is inserted (composition silently cancelled). Typing slowly, or repeating the same keystrokes, sometimes works — intermittent.

Root cause analysis

The identifier input is a controlled component, and its change handler mutates the value on every input event, including events fired during IME composition:

packages/experience/src/shared/components/InputFields/SmartInputField/use-smart-input-field.ts

const onInputValueChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
  ({ target: { value } }) => {
    const trimValue = value.trim();
    // ...
    setInputValue(trimValue);
    // ...
  },
  ...
);

SmartInputField then renders <input value={inputValue} ...> as a controlled input.

When React re-renders while composition is still in progress, the DOM value (which includes the latest uncommitted pre-edit text, e.g. "zh") differs from the state written by the handler (e.g. "z"). React DOM therefore writes the stale controlled value back into the input, and Chromium cancels the ongoing IME composition session on programmatic value writes during pre-edit. This explains the intermittency: it is a race between keystrokes and re-renders — if the next key arrives before the re-render, the write lands mid-composition and kills the candidate window; if not, composition survives.

Suggested fix

Do not mutate the controlled value while the input is composing. Options:

  • Check event.isComposing (or nativeEvent.isComposing) in onInputValueChange and skip the trim()/setInputValue rewrite during composition, finalizing on compositionend; and/or
  • Track compositionstart / compositionend and defer value normalization (trim) until after composition ends (or on blur / submit).

The same pattern applies to the phone-number branch which also rewrites the value on change.

Environment

  • Logto version: 1.41.0 (self-hosted, Docker svhd/logto); the responsible code is unchanged on master as of 2026-09-21
  • OS: Windows 11 (25H2)
  • Browsers: latest Chrome and Edge (also reproduces inside an Electron 43 WebView)
  • IME: Microsoft Pinyin (Chinese, full-mode). Likely affects any CJK IME that uses composition (Sogou, QQ Pinyin, Japanese, Korean)

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions