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
Enable 3G Network throttling to simulate slow lazy-loading
Two steps in quick succession: switch to "Buckets" and then to "Test", before "Buckets" finishes loading
Expected
"Test" UI (test.jsx) is visible.
Actual
"Buckets" UI (buckets.jsx) is visible.
Additionally, if you try to go to "Buckets" UI and then back to "Test", still, "Buckets" UI will be rendered. This requires a full page reload.
Video reproduction (w/ 3G throttling enabled)
2026-01-08.17-52-01.mp4
Additional notes
1 - If you let "Buckets" load, then switch to "Test", everything works as expected:
2026-01-08.17-54-24.mp4
2 - If we had three routes, e.g. "Buckets", "Documents", "Test", and with the throttling, we let "Buckets" load after navigating to it, then quickly switched between "Documents" and "Test", we'd see the "Documents" UI issue persisting on "Test" route, but it "Buckets" route would correctly load, since it was already pre-loaded.
Workaround
The code for workaround is implemented in the replit/zip. You only need to switch the lazyLoad (uncomment it in replit code).
Details
// Fix for lazy load race condition: track the latest transition// and discard stale lazy load resultsletlatestLazyLoadId=0;// Now the fix works by://// 1. Tracking a global latestLazyLoadId - incremented each time any lazy load starts// 2. Each lazy load captures its ID - when the import() starts// 3. After import completes, check if stale - if myId !== latestLazyLoadId, a newer navigation has started// 4. Reject stale loads - returns Promise.reject(transition.abort()) which aborts the transition and prevents the stale states from being registered//// This ensures that if you click Test → Buckets quickly// :// - Test's lazy load gets ID 1// - Buckets' lazy load gets ID 2// - When Test's import() resolves, it checks: 1 !== 2, so it aborts itself// - Only Buckets' lazy load proceeds to register its states//// You can test this by throttling to 3G and rapidly switching between routes - only the last clicked route should render.functioncreateTrackedLazyLoad(importFn){return(transition)=>{constmyId=++latestLazyLoadId;returnimportFn().then((module)=>{// If a newer navigation started, reject this stale lazy loadif(myId!==latestLazyLoadId){returnPromise.reject(transition.abort());}returnmodule;});};}constbucketsState={name: "app.admin.buckets.**",url: "/buckets",lazyLoad: createTrackedLazyLoad(()=>import("./buckets.js")),};// ... Also apply to testState
Hi, thank you for all the work on the router 🙏🏻 I've found an issue during development.
Code created from the Hello World UI router example:
Steps to reproduce
/route: https://react-javascript--piotrnajda3000.replit.app/Expected
"Test" UI (test.jsx) is visible.
Actual
"Buckets" UI (buckets.jsx) is visible.
Additionally, if you try to go to "Buckets" UI and then back to "Test", still, "Buckets" UI will be rendered. This requires a full page reload.
Video reproduction (w/ 3G throttling enabled)
2026-01-08.17-52-01.mp4
Additional notes
1 - If you let "Buckets" load, then switch to "Test", everything works as expected:
2026-01-08.17-54-24.mp4
2 - If we had three routes, e.g. "Buckets", "Documents", "Test", and with the throttling, we let "Buckets" load after navigating to it, then quickly switched between "Documents" and "Test", we'd see the "Documents" UI issue persisting on "Test" route, but it "Buckets" route would correctly load, since it was already pre-loaded.
Workaround
The code for workaround is implemented in the replit/zip. You only need to switch the lazyLoad (uncomment it in replit code).
Details
With workaround implemented:
2026-01-08.18-02-05.mp4
Thank you!