Repository navigation
[$250] Chat - Focus in composer is lost after sending a message #83766
Description
Activity
- addedDeployBlockerCashThis issue or pull request should block deploymentThis issue or pull request should block deploymentExternalAdded to denote the issue can be worked on by a contributorAdded to denote the issue can be worked on by a contributorDailyKSv2KSv2BugSomething is broken. Auto assigns a BugZero manager.Something is broken. Auto assigns a BugZero manager.
on Feb 27, 2026 - addedHelp WantedApply this label when an issue is open to proposals by contributorsApply this label when an issue is open to proposals by contributors
on Feb 27, 2026 Triggered auto assignment to Contributor-plus team member for initial proposal review - @sobitneupane (
External)Triggered auto assignment to @kadiealexander (
Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details. Please add this bug to a GH project, as outlined in the SO.- changed the title
[-]Chat - Focus in composer is lost after sending a message[/-][+][$250] Chat - Focus in composer is lost after sending a message[/+]on Feb 27, 2026 Triggered auto assignment to @yuwenmemon (
DeployBlockerCash), see https://stackoverflowteams.com/c/expensify/questions/9980/ for more details.Job added to Upwork: https://www.upwork.com/jobs/~022027507279877883330
6 remaining items
🔍 Issue Analysis
Summary: After sending a message in chat on desktop web (Windows Chrome), the composer loses focus. This is a regression in staging v9.3.27-0, not present in production. A closely related deploy blocker — Expensify/App#83731 — reports the same symptom on mWeb (compose box closing after send).
Root Cause: PR Expensify/App#81280 ("Report Actions Derived Value") introduces an async Onyx-derived value
VISIBLE_REPORT_ACTIONSthat causes a double re-render ofReportActionsViewafter each message send. The sequence is:- User sends a message → optimistic
REPORT_ACTIONSupdate fires → first re-render (action haspendingAction, so it bypasses the derived cache and is visible). - The async
VISIBLE_REPORT_ACTIONSderived value recomputes, but itsOnyx.set()write is async, so the component receives the update after a delay. - When the API success callback clears
pendingAction: null, the action switches to the derived cache path. If the cache hasn't propagated yet, a timing guard (hasDerivedValueTimingIssueatsrc/pages/inbox/report/ReportActionsView.tsx:333) briefly replaces the entireReportActionsListwith a non-animated skeleton. - This rapid DOM unmount/remount of the action list (a sibling of the composer) causes a layout reflow that triggers the browser to remove focus from the composer input.
A contributing factor is PR Expensify/App#76104, which changed
overflow: 'hidden'tooverflow: 'clip'insrc/styles/utils/chatContentScrollViewPlatformStyles/index.ts.overflow: clipdoes not create a block formatting context, making the layout less stable during rapid re-renders — this is especially impactful on mWeb Android Chrome (where the virtual keyboard dismisses), but also destabilizes desktop layout during the skeleton flash.Recommended Fix: Two options, in order of risk:
- Quickest fix (low risk): In
ReportActionsView.tsx, whenhasDerivedValueTimingIssueis true, use the previousvisibleReportActionsvalue as a fallback instead of rendering a skeleton. This prevents the DOM unmount/remount entirely and keeps the action list stable during the derived-value propagation delay. - Alternative: Revert
overflow: 'clip'back tooverflow: 'hidden'insrc/styles/utils/chatContentScrollViewPlatformStyles/index.ts(from PR fix: Allow multiple loads inuseLoadReportActionshook #76104). This restores the block formatting context and stabilizes layout during re-renders. This is the fix proposed on the related issue [$250] mWeb – Compose box closes after sending messages #83731.
Open Questions
- Runtime confirmation would help determine whether the
hasDerivedValueTimingIssueskeleton actually renders on desktop web after message send, or whether there is a separate mechanism causing focus loss on desktop Chrome specifically. - On medium-width desktops (< 1300px) with the side panel visible,
isSidePanelHiddenOrLargeScreenisfalse, which blocks ALL focus restoration in the composer (ComposerWithSuggestions.tsx:790). If focus is lost under these conditions, no backup mechanism can recover it.
Detailed Investigation
Normal Focus Retention Flow (Before Regression)
When a user sends a message, focus is retained through these mechanisms:
- Enter key path: Keyboard events don't blur the input (
ComposerWithSuggestions.tsx:520-523) - Send button click path:
onMouseDown={(e) => e.preventDefault()}on the SendButton parent View (SendButton.tsx:39-40) prevents the browser from transferring focus to the button - The worklet clear (
ComponentUtils/index.ts:11-18) callsinput.clear()without triggering blur - Backup:
focusComposerOnKeyPresslistener (ComposerWithSuggestions.tsx:702-726) refocuses composer when any printable key is pressed
These mechanisms are all still intact in the current code. The focus loss is caused by a new side effect (the derived value double re-render), not by a broken focus mechanism.
Evidence Chain
Step What Happens Evidence 1 User sends message handleSendMessageinReportActionCompose.tsx:4692 Optimistic action added with pendingAction: ADDReport/index.ts:743-7553 First re-render — action visible (bypasses cache) ReportActionsUtils.ts:1209—if (reportAction.pendingAction)4 Derived value starts async recompute OnyxDerived/index.ts:80-83— sync compute, asyncOnyx.set()5 API success clears pendingAction: nullReport/index.ts:743-755successData6 Second re-render — action goes through cache path ReportActionsUtils.ts:1213— usesvisibleReportActionscache7 If cache stale → hasDerivedValueTimingIssuetriggers → skeletonReportActionsView.tsx:333-3368 ReportActionsList unmounts/remounts → layout reflow → focus lost Browser behavior during significant DOM changes Why Desktop Web Is Affected
- FocusTrap is inactive for the Report screen in wide layout (
WIDE_LAYOUT_INACTIVE_SCREENS.ts:10), so it's not stealing focus - FocusTrap onActivate protects INPUT/TEXTAREA from blur (
FocusTrapForScreen/index.web.tsx:46-48), even if it were active - The composer (in
ReportFooter) is a sibling ofReportActionsViewinReportScreen.tsx:1032-1067, so the skeleton doesn't unmount the composer itself - However, the rapid layout reflow from the action list disappearing/reappearing can cause Chrome to drop focus from the active input element — this is browser-specific behavior
PRs Involved
PR Title Risk Level Expensify/App#81280 Report Actions Derived Value High — async double re-renders + skeleton guard causing DOM thrash Expensify/App#76104 Allow multiple loads in useLoadReportActions Medium — overflow: clipdestabilizes layout during re-rendersRelevant Code
src/pages/inbox/report/ReportActionsView.tsx:105— subscribes to derived valuesrc/pages/inbox/report/ReportActionsView.tsx:220-242—visibleReportActionsuseMemo withvisibleReportActionsDatadependencysrc/pages/inbox/report/ReportActionsView.tsx:333-336—hasDerivedValueTimingIssueskeleton guardsrc/libs/ReportActionsUtils.ts:1206-1232—isReportActionVisiblewith derived value fallbackssrc/libs/actions/OnyxDerived/configs/visibleReportActions.ts— derived value configsrc/styles/utils/chatContentScrollViewPlatformStyles/index.ts—overflow: 'clip'changesrc/pages/inbox/report/ReportActionCompose/SendButton.tsx:39-40— focus prevention (still intact)src/pages/inbox/report/ReportActionCompose/ComposerWithSuggestions/ComposerWithSuggestions.tsx:790—isSidePanelHiddenOrLargeScreenfocus guard
Confidence: Medium-High
The analysis of the async double re-render mechanism is well-supported by code evidence and aligns with the related mWeb issue (#83731). The specific mechanism by which Chrome drops focus during layout reflow on desktop is a browser-level behavior that would need runtime evidence (e.g., a Chrome DevTools focus monitor) to fully confirm.
Note: This issue is closely related to deploy blocker Expensify/App#83731 (mWeb compose box closing after send), which has the same root cause. A fix for one should likely address both.
Next Steps for Contributor+ team: Reply with
@MelvinBot implement thisto create a draft PR, or@MelvinBot <your feedback>to refine this analysis.- User sends a message → optimistic
I’m working on this now and will open a PR shortly.
Not reproducible on staging or dev as well
Screen.Recording.2026-03-02.at.08.49.35.mov
@MobileMage do you believe this bug/regression is linked to our PR? Thank you.
No, I don't think so @brunovjk
Reacted by Bruno RochaThis is regression from #76104 and already fixed by this revert
Reacted by Andrew Rosiclair and Bruno RochaProposal
Please re-state the problem that we are trying to solve in this issue.
On web, after sending a chat message, the composer input loses focus. This breaks the expected “type → send → keep typing” workflow and forces the user to click the composer again before sending the next message.
What is the root cause of that problem?
We currently rely on “nothing blurs the input” to keep focus after submit (e.g.,
preventDefault()on the send button click path andpreventDefault()for Enter). In staging, the post-submit UI update (clearing the input + optimistic action render/layout changes) can still cause the browser to drop focus from the underlying<textarea>/<input>. Since we don’t explicitly restore focus after a successful submit, the composer stays blurred until the user manually refocuses it (and some existing fallback focus paths are intentionally gated by side-panel/layout checks, so they don’t help once focus is lost).What changes do you think we should make in order to solve the problem?
Add an explicit “restore focus after send” step on web when the composer was focused at the moment the user sent the message.
- Track whether the composer was focused when
handleSendMessage()runs (e.g., viacomposerRef.current?.isFocused()inReportActionCompose). - After the submit path completes (the
onClear→submitForm()flow), ifwasFocusedOnSendis true, callcomposerRef.current?.focus()to re-focus the input. - Keep it scoped to the “send message” path so we don’t steal focus during other re-renders, and avoid focusing when the user intentionally sent while not in the composer.
What alternative solutions did you explore? (Optional)
- Making the send button non-focusable: it’s already guarded (
onMouseDown+focusable={false}), and the issue still reproduces. - Broadening existing “focus-on-keypress / focus-on-screen-focus” logic: those paths are meant for different scenarios and are gated by layout/state checks; using them here would either be ineffective or risk focusing at the wrong times.
- Track whether the composer was focused when
Not reproducible in v9.3.27-5
- removedDeployBlockerCashThis issue or pull request should block deploymentThis issue or pull request should block deployment
on Mar 2, 2026
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsDone
If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!
Version Number: 9.3.27-0
Reproducible in staging?: Yes
Reproducible in production?: No
If this was caught during regression testing, add the test name, ID and link from BrowserStack: https://test-management.browserstack.com/projects/2219752/folder/13176925/test-cases/41237591
Email or phone of affected tester (no customers): fischer9966+053122@gmail.com
Issue reported by: Applause Internal Team
Bug source: Regression TC Execution
Device used: MacOS 26.3 / Safari, Chrome
App Component: Chat Report View
Action Performed:
Expected Result:
After sending the message, the composer field is cleared and the focus is set to the beginning of the line
Actual Result:
Focus in composer is lost after sending the message.
Workaround:
Unknown
Platforms:
Screenshots/Videos
Bug7090428_1772229140450.Web-Chat-Focus-lost-after-post.mp4
View all open jobs on GitHub
Issue Owner
Current Issue Owner: @sobitneupaneUpwork Automation - Do Not Edit