bug(ui): Enter key submits forms while typing in Japanese, Chinese, or Korean (IME composition)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 39.9k
- Forks
- 3.4k
- Avg merge
- 6h 51m
- Merged PRs (30d)
- 232
Description
When users type using an Input Method Editor (IME), commonly used for Japanese, Chinese, Korean, and several other languages, pressing Enter to confirm a character candidate also submits the surrounding form or action. This makes a number of inputs across the PostHog UI difficult or impossible to use without switching IME off, which is impractical.
For example, typing ue in Japanese IME shows candidates like 上 / 飢え / 植え. Pressing Enter is supposed to confirm the highlighted candidate. Instead, the page also fires its primary action, creating the organization / sending the invite / running the query / saving the title.
A previous fix in PR #51350 addressed this in the LemonInput and LemonTextArea components, but only when consumers use the onPressEnter prop. Many places in the codebase wire Enter via the lower-level onKeyDown prop (or build on top of more primitive components), and those still hit the bug. A one-off follow-up landed for the AI-first home input in PR #60043, but the broader audit needs a single tracking issue.
How to reproduce
- Open one of the inputs listed below (the "Create organization" modal is a quick example)
- Switch your OS input method to a Japanese, Chinese, or Korean IME (or any IME that uses Enter to confirm)
- Type characters that produce candidates (for example,
uein Japanese) - Press Enter to confirm a candidate
- Observed: the form submits, the action fires, or the highlighted suggestion is selected
- Expected: Enter only confirms the IME candidate. Pressing Enter again on the already-confirmed (non-composing) text should then submit as normal
Where it shows up
This is a single root cause producing many user-visible bugs. Highest impact (form-submitting / action-firing inputs):
- Creating an organization or project
- Sending team invites
- Authorizing an OAuth client
- Saving a person property
- Saving a scene, notebook, or workflow step title
- Saving an inline-edited field (used widely across the UI)
- Submitting a Lemon dialog form
- Adding an authorized domain (Conversations)
- Inserting a link in the support editor (Conversations)
- Marketing-analytics attribution settings
- Heatmaps browser URL bar
- Jump-to-timestamp form
- Screenshot annotation
- Tag inputs that add a tag on Enter (
LemonInputSelect)
Lower impact (the wrong item gets selected, but no destructive action):
- Taxonomic filter (combobox, infinite list, autocomplete input)
- Max slash-command autocomplete
- Markdown editor inline slash commands
A grep of e.key === 'Enter' across frontend/src/ and products/*/frontend/ after filtering out a11y key === 'Enter' || key === ' ' handlers and intentional modifier shortcuts (metaKey + Enter, ctrlKey + Enter) gives roughly 20 to 25 sites. The exact line numbers as of this writing are catalogued in the additional context section below.
Why it happens
LemonInput and LemonTextArea define their own internal onKeyDown that includes the !e.nativeEvent.isComposing IME guard, but then spread {...props} immediately after, so a consumer-provided onKeyDown overrides the internal one. See https://github.com/PostHog/posthog/blob/master/frontend/src/lib/lemon-ui/LemonInput/LemonInput.tsx#L264-L272.
This makes the guard apply only to consumers that use onPressEnter. Anyone using onKeyDown (which is allowed by the component's prop types) silently loses the guard. The same shape exists in LemonTextArea. Components built directly on TextareaPrimitive, Autocomplete.Input, or native inputs never had the guard at all.
Suggested approaches
Not mutually exclusive:
- Per-site fix: add
&& !e.nativeEvent.isComposingto each Enter check across the codebase. Mechanical, easy to review, can ship incrementally. - Library-level fix: change
LemonInputandLemonTextAreato wrap (not replace) the consumer'sonKeyDownso the IME guard always applies. Needs care because the components also handlestopPropagationandonPressEnter, and the consumer's handler should still receive non-Enter keys.
Additional context
Specific file references (line numbers may drift):
| File | Line | What Enter does |
|---|---|---|
frontend/src/scenes/organization/CreateOrganizationModal.tsx |
77 | Creates org |
frontend/src/scenes/project/CreateProjectModal.tsx |
119 | Creates project |
frontend/src/scenes/settings/organization/InviteModal.tsx |
231, 249 | Sends team invites |
frontend/src/scenes/oauth/OAuthAuthorize.tsx |
98 | Confirms OAuth name |
frontend/src/scenes/persons/NewProperty.tsx |
121 | Saves person property |
frontend/src/scenes/web-analytics/tabs/marketing-analytics/frontend/components/settings/AttributionSettings.tsx |
126 | Submits attribution setting |
frontend/src/scenes/heatmaps/components/HeatmapsBrowser.tsx |
130 | Navigates to URL |
frontend/src/layout/scenes/components/SceneTitleSection.tsx |
512 | Saves scene title |
frontend/src/scenes/notebooks/Nodes/components/NotebookNodeTitle.tsx |
116 | Saves notebook node title |
frontend/src/scenes/notebooks/Nodes/NotebookNodeLatex.tsx |
76 | Confirms LaTeX |
frontend/src/scenes/surveys/hosted-canvas/InlineEditable.tsx |
85 | Saves inline-edited field |
frontend/src/lib/components/EditableField/EditableField.tsx |
167 | Saves editable field |
frontend/src/lib/lemon-ui/LemonDialog/LemonDialog.tsx |
213, 219 | Submits dialog form |
frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx |
549 | Adds typed tag/selection |
frontend/src/lib/components/SearchAutocomplete/SearchAutocomplete.tsx |
215 | Confirms autocomplete selection |
frontend/src/lib/components/DateFilter/JumpToTimestampForm.tsx |
50 | Jumps to typed timestamp |
frontend/src/lib/components/TakeScreenshot/ScreenShotEditor.tsx |
503 | Screenshot text input |
frontend/src/scenes/product-tours/components/NewProductTourModal.tsx |
111 | Saves new product tour |
products/conversations/frontend/scenes/settings/AuthorizedDomains.tsx |
43 | Adds authorized domain |
products/conversations/frontend/components/Editor/SupportEditor.tsx |
532 | Inserts link |
products/workflows/frontend/Workflows/hogflows/steps/components/StepView.tsx |
127, 173 | Saves workflow step name |
Combobox-style inputs (lower impact):
frontend/src/lib/components/TaxonomicFilter/menu/Combobox.tsx:377frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx:485frontend/src/lib/components/TaxonomicFilter/headless/AutocompleteInput.tsx:990frontend/src/scenes/max/components/SlashCommandAutocomplete.tsx:64frontend/src/lib/components/MarkdownEditor/inline/inlineMarkdownSlashCommands.tsx:360
Related work:
- Original fix: PR #51350
- AI-first home input follow-up: PR #60043
Debug info
- PostHog Cloud, Debug information: [please copy/paste from https://us.posthog.com/settings/project-details#variables or https://eu.posthog.com/settings/project-details#variables]
- PostHog Hobby self-hosted with
docker compose, version/commit: [please provide] - PostHog self-hosted with Kubernetes (deprecated, see
Sunsetting Kubernetes support), version/commit: [please provide]
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with frontend/src/lib/lemon-ui/LemonInput/LemonInput.tsx and LemonTextArea, then review the Enter handlers listed in the issue across frontend/src and products/*/frontend/. Compare the existing PR #51350 behavior and reproduce with a Japanese, Chinese, or Korean IME. Done means composing Enter confirms the candidate without firing an action, while a later non-composing Enter retains normal behavior across the affected inputs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- accessibility, frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100