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
- Self-hosted Logto (Docker image
svhd/logto:1.41.0) with sign-in identifier username enabled (the combined "username / email / phone" input is shown).
- On Windows, activate Microsoft Pinyin (Chinese) input mode.
- Open the sign-in page (via a normal OIDC authorize flow so an interaction exists) and focus the identifier input.
- Type a pinyin syllable quickly, e.g.
z h e.
- 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)
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
svhd/logto:1.41.0) with sign-in identifierusernameenabled (the combined "username / email / phone" input is shown).zhe.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.tsSmartInputFieldthen 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:
event.isComposing(ornativeEvent.isComposing) inonInputValueChangeand skip thetrim()/setInputValuerewrite during composition, finalizing oncompositionend; and/orcompositionstart/compositionendand 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
svhd/logto); the responsible code is unchanged onmasteras of 2026-09-21