You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Due for payment 2026-03-31] Tab name does not persist after changing section and back to the previous section #85352
Version Number: 9.3.38-0 Reproducible in staging?: Yes Reproducible in production?: N/A - New feature If this was caught during regression testing, add the test name, ID and link from BrowserStack:#83388 Email or phone of affected tester (no customers):sdijnsoijsodosj@gmail.com Issue reported by: Applause Internal Team Bug source: Exploratory - Significant User Experience Deterioration Device used: Mac 26.2 / Chrome App Component: Other
Action Performed:
Go to staging.new.expensify.com
Go to Home tab.
Go to Inbox.
→ Tab name shows report name.
Open a different report.
Open the same report in Step 3.
→ Tab name changes to New Expensify.
Go to workspace settings > Overview.
→ Tab name shows workspace name - Overview.
Go to Members, then go back to Overview.
→ Tab name changes to New Expensify.
Go to Account > Profile.
→ Tab name shows Profile.
Go to Subscription and back to Profile.
→ Tab name changes New Expensify.
Expected Result:
In Step 5, 7 and 9, tab will show currently opened page.
Actual Result:
In Step 5, 7 and 9, tab name does not persist and shows New Expensify.
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
Identify the pull request that introduced this issue and revert it.
Classification: Frontend bug Causing PR: #83388 - "Extend useDocumentTitle hook to all remaining pages" by @MelvinBot (High confidence) Related Issues: #83378
Recommendation: REVERT
The bug is caused by a race condition in the useDocumentTitle hook implementation. When navigating between sections (e.g., Overview → Members → Overview), the cleanup function clears the title but the focus effect does not reliably restore it when returning to the same page.
Labels: No label changes needed - keeping DeployBlockerCash as this is a frontend bug in the App repository requiring an App PR fix.
📋 Detailed Analysis
Evidence
PR #83388 introduced a new useDocumentTitle hook and applied it to 27+ pages across the app. The issue reports that tab names don't persist when navigating between sections, which matches exactly when this PR was merged (version 9.3.38-0).
Verification
The PR modified the exact affected areas: workspace pages (Overview, Members, etc.), domain pages, and settings pages
The issue reproduction steps match the pages modified in this PR
The bug is in /home/runner/_work/App/App/src/hooks/useDocumentTitle.ts:
functionuseDocumentTitle(title: string){useFocusEffect(useCallback(()=>{setPageTitle(title);return()=>setPageTitle('');// ← This clears the title on blur},[title]),);}
The Problem:
When navigating from "Overview" to "Members", the Overview page loses focus
The cleanup function runs: setPageTitle('') clears the title to empty string
When navigating back to Overview, useFocusEffect should call setPageTitle(title) again
However, there's a race condition - the cleanup runs but the re-focus doesn't reliably restore the title
Result: currentPageTitle remains empty, so the document title falls back to "New Expensify"
This explains why:
Step 5 (opening same report twice): title changes to "New Expensify"
Step 7 (Overview → Members → Overview): title changes to "New Expensify"
Step 9 (Profile → Subscription → Profile): title changes to "New Expensify"
The cleanup function return () => setPageTitle('') is too aggressive and creates a timing issue with React Navigation's focus effects.
Please re-state the problem that we are trying to solve in this issue.
When navigating between sections and then returning to a previously visited page, the browser tab title reverts to "New Expensify" instead of showing the page-specific title. This happens in all three reported scenarios: Inbox reports, workspace settings pages, and account settings pages.
What is the root cause of that problem?
The root cause is a race condition between the cleanup function in useDocumentTitle and the asynchronous title-update pipeline.
In src/hooks/useDocumentTitle.ts:9, the useFocusEffect cleanup calls setPageTitle('') whenever the screen loses focus (blurs):
useFocusEffect(useCallback(()=>{setPageTitle(title);return()=>setPageTitle('');// ← clears title to empty on blur},[title]),);
Each setPageTitle call itself queues a setTimeout(fn, 0) inside updateDocumentTitle()
Inside updateDocumentTitle() (line 33), the fallback logic currentPageTitle || CONFIG.SITE_TITLE means any call while currentPageTitle is empty will set document.title to "New Expensify".
Debounced unread update fires 300ms later → updateDocumentTitle() → queues yet another setTimeout
The exact ordering of these setTimeout(fn, 0) callbacks relative to when currentPageTitle gets read depends on React's effect lifecycle, the split navigator's route management, and whether screens are unmounted vs kept alive. In practice, there are timing windows — particularly during React's effect cleanup/setup phase when navigating back in split navigators — where the debounced triggerUnreadUpdate or popstate listener fires while currentPageTitle is still empty from the blur cleanup, overwriting the title to "New Expensify".
What changes do you think we should make in order to solve the problem?
Remove the cleanup function from useDocumentTitle — change src/hooks/useDocumentTitle.ts to:
functionuseDocumentTitle(title: string){useFocusEffect(useCallback(()=>{setPageTitle(title);// No cleanup — let the next focused page overwrite the title},[title]),);}
This eliminates the race condition entirely. When navigating between pages, each page simply sets its own title on focus. The previous page's title naturally gets overwritten — there's no need to clear it to an empty string first.
Since PR #83388 extended useDocumentTitle to all major pages, every page will set its own title on focus. For any transient or intermediate screens that don't call useDocumentTitle, the previous page's title persisting briefly is vastly preferable to showing "New Expensify" incorrectly.
What alternative solutions did you explore? (Optional)
Cancel/ignore stale setTimeout callbacks — could use a generation counter or AbortController pattern in updateDocumentTitle. This adds complexity and doesn't address the fundamental issue that currentPageTitle is cleared unnecessarily.
Replace useFocusEffect with useEffect — would tie the title to component lifecycle (mount/unmount) instead of focus/blur. However, in React Navigation, screens are often kept mounted in the stack, so useEffect wouldn't re-set the title on refocus.
Set cleanup to the default title instead of empty — change cleanup to return () => setPageTitle(CONFIG.SITE_TITLE). This prevents the fallback logic from being triggered but still has unnecessary title thrashing during transitions.
The proposed fix (removing cleanup) is the simplest and most robust.
Next Steps for Contributor+ team: Reply with @MelvinBot implement this to create a draft PR, @MelvinBot <your feedback> to refine this analysis, or explain why you are rejecting Melvin's proposal.
Skipping the payment summary for this issue since all the assignees are employees or vendors. If this is incorrect, please manually add the payment summary SO.
Activity
melvin-bot commentedon Mar 16, 2026
You have been assigned to this deploy blocker because you recently merged this PR: #83388
melvin-bot commentedon Mar 16, 2026
💬 A slack conversation has been started in #expensify-open-source
github-actions commentedon Mar 16, 2026
👋 Friendly reminder that deploy blockers are time-sensitive ⏱ issues! Check out the open `StagingDeployCash` deploy checklist to see the list of PRs included in this release, then work quickly to do one of the following:
OSBotify commentedon Mar 16, 2026
🔍 Investigation Summary
Classification: Frontend bug
Causing PR: #83388 - "Extend useDocumentTitle hook to all remaining pages" by @MelvinBot (High confidence)
Related Issues: #83378
Recommendation: REVERT
The bug is caused by a race condition in the
useDocumentTitlehook implementation. When navigating between sections (e.g., Overview → Members → Overview), the cleanup function clears the title but the focus effect does not reliably restore it when returning to the same page.Labels: No label changes needed - keeping
DeployBlockerCashas this is a frontend bug in the App repository requiring an App PR fix.📋 Detailed Analysis
Evidence
PR #83388 introduced a new
useDocumentTitlehook and applied it to 27+ pages across the app. The issue reports that tab names don't persist when navigating between sections, which matches exactly when this PR was merged (version 9.3.38-0).Verification
Root Cause
The bug is in
/home/runner/_work/App/App/src/hooks/useDocumentTitle.ts:The Problem:
setPageTitle('')clears the title to empty stringuseFocusEffectshould callsetPageTitle(title)againcurrentPageTitleremains empty, so the document title falls back to "New Expensify"This explains why:
The cleanup function
return () => setPageTitle('')is too aggressive and creates a timing issue with React Navigation's focus effects.MelvinBot commentedon Mar 16, 2026
Proposal
Please re-state the problem that we are trying to solve in this issue.
When navigating between sections and then returning to a previously visited page, the browser tab title reverts to "New Expensify" instead of showing the page-specific title. This happens in all three reported scenarios: Inbox reports, workspace settings pages, and account settings pages.
What is the root cause of that problem?
The root cause is a race condition between the cleanup function in
useDocumentTitleand the asynchronous title-update pipeline.In
src/hooks/useDocumentTitle.ts:9, theuseFocusEffectcleanup callssetPageTitle('')whenever the screen loses focus (blurs):This clears the module-level
currentPageTitlevariable to an empty string insrc/libs/UnreadIndicatorUpdater/updateUnread/index.ts:8.Meanwhile, there are multiple asynchronous callers of
updateDocumentTitle()that can fire whilecurrentPageTitleis empty:src/libs/UnreadIndicatorUpdater/index.ts:106-108) — triggers a debounced (300ms) call toupdateDocumentTitle()on every navigationpopstatelistener (src/libs/UnreadIndicatorUpdater/updateUnread/index.ts:51-53) — triggers a non-debounced call on browser back/forwardsetPageTitlecall itself queues asetTimeout(fn, 0)insideupdateDocumentTitle()Inside
updateDocumentTitle()(line 33), the fallback logiccurrentPageTitle || CONFIG.SITE_TITLEmeans any call whilecurrentPageTitleis empty will setdocument.titleto "New Expensify".The race condition manifests because:
setPageTitle('')→currentPageTitle = ''→ queuessetTimeoutsetPageTitle(title)→currentPageTitle = title→ queuessetTimeoutupdateDocumentTitle()→ queues yet anothersetTimeoutThe exact ordering of these
setTimeout(fn, 0)callbacks relative to whencurrentPageTitlegets read depends on React's effect lifecycle, the split navigator's route management, and whether screens are unmounted vs kept alive. In practice, there are timing windows — particularly during React's effect cleanup/setup phase when navigating back in split navigators — where the debouncedtriggerUnreadUpdateorpopstatelistener fires whilecurrentPageTitleis still empty from the blur cleanup, overwriting the title to "New Expensify".What changes do you think we should make in order to solve the problem?
Remove the cleanup function from
useDocumentTitle— changesrc/hooks/useDocumentTitle.tsto:This eliminates the race condition entirely. When navigating between pages, each page simply sets its own title on focus. The previous page's title naturally gets overwritten — there's no need to clear it to an empty string first.
Since PR #83388 extended
useDocumentTitleto all major pages, every page will set its own title on focus. For any transient or intermediate screens that don't calluseDocumentTitle, the previous page's title persisting briefly is vastly preferable to showing "New Expensify" incorrectly.What alternative solutions did you explore? (Optional)
Cancel/ignore stale
setTimeoutcallbacks — could use a generation counter or AbortController pattern inupdateDocumentTitle. This adds complexity and doesn't address the fundamental issue thatcurrentPageTitleis cleared unnecessarily.Replace
useFocusEffectwithuseEffect— would tie the title to component lifecycle (mount/unmount) instead of focus/blur. However, in React Navigation, screens are often kept mounted in the stack, souseEffectwouldn't re-set the title on refocus.Set cleanup to the default title instead of empty — change cleanup to
return () => setPageTitle(CONFIG.SITE_TITLE). This prevents the fallback logic from being triggered but still has unnecessary title thrashing during transitions.The proposed fix (removing cleanup) is the simplest and most robust.
Next Steps for Contributor+ team: Reply with
@MelvinBot implement thisto create a draft PR,@MelvinBot <your feedback>to refine this analysis, or explain why you are rejecting Melvin's proposal.IuliiaHerets commentedon Mar 16, 2026
Chrome / v9.3.38-1
Issue is still reproduced
bandicam.2026-03-16.14-59-29-446.mp4
yuwenmemon commentedon Mar 16, 2026
Ah, sorry, this was not from that other PR I got overzealous with the copy-paste.
This is actually not a blocker and could be fixed in a follow-up PR.
23 remaining items
melvin-bot commentedon Mar 24, 2026
Triggered auto assignment to @mallenexpensify (
Awaiting Payment)melvin-bot commentedon Mar 31, 2026
Skipping the payment summary for this issue since all the assignees are employees or vendors. If this is incorrect, please manually add the payment summary SO.
thesahindia commentedon Aug 6, 2026
This caused #95208. Details are in #96916 description.