fix: scope router to basepath for MFE support#6063
fix: scope router to basepath for MFE support#6063Diveafall wants to merge 1 commit intoTanStack:mainfrom
Conversation
When a router has a basepath configured, it now only processes location changes that are within its basepath scope. This enables micro-frontend architectures where multiple TanStack routers coexist. Changes: - Add isPathInScope() utility to check if a path is within basepath scope - Add basepath check at start of router.load() method - Add comprehensive unit tests for isPathInScope() The implementation mirrors React Router's stripBasename behavior where paths outside the basename scope are silently ignored. Fixes TanStack#2103 Fixes TanStack#2108 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
WalkthroughAdded a new Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (3)
🧰 Additional context used📓 Path-based instructions (2)**/*.{ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*.{js,ts,tsx}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (5)📓 Common learnings📚 Learning: 2025-10-01T18:30:26.591ZApplied to files:
📚 Learning: 2025-10-08T08:11:47.088ZApplied to files:
📚 Learning: 2025-10-09T12:59:02.129ZApplied to files:
📚 Learning: 2025-12-06T15:03:07.223ZApplied to files:
🧬 Code graph analysis (2)packages/router-core/tests/path.test.ts (1)
packages/router-core/src/router.ts (1)
🔇 Additional comments (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
| } | ||
|
|
||
| load: LoadFn = async (opts?: { sync?: boolean }): Promise<void> => { | ||
| // If this router has a basepath, only respond to paths within scope. |
There was a problem hiding this comment.
if we add support for opting out of navigation, we need to check where that should happen best.
if that's here, then i would rather add an optional callback function that can execute arbitrary user code instead of adding this logic here.
There was a problem hiding this comment.
do you mean adding an optional callback that decides whether navigation should occur or not?
There was a problem hiding this comment.
I can make that change, I just want to make sure that Tanstack Router is OK with intercepting changes outside of its route boundaries setup by basepath.
There was a problem hiding this comment.
These can be combined also - basepath scoping + optional callback for user overrides.
Question is: is basepath scoping a fundamental enough expectation for it to be the default?
There was a problem hiding this comment.
as i wrote above, we first need to identify the correct location of such a change.
i feel like history would be better suited. did you have a look at it?
in essence you are building a blocker functionality that history already has
|
Thanks for the suggestion - I explored the history blocker API and I see what you mean about keeping this at the history level. However, I noticed a semantic difference between blockers and what MFE basepath scoping needs:
With the current blocker API, returning A few options I can think of:
Which approach aligns best with how you see the history API evolving? Or did you have something else in mind? |
Problem
When multiple TanStack routers coexist in a micro-frontend (MFE) architecture, all routers respond to all history changes, even when the path is outside their configured
basepath. This causes unexpected behavior:defaultNotFoundComponenttriggers incorrectlyRoot Cause
In
Transitioner.tsx(line 44), every router subscribes to history changes via:This means when the shell navigates to
/settings, an MFE router withbasepath: '/user-management'still receives the event and attempts to match/settingsagainst its routes - which fails, triggering a 404.Why This Happens
TanStack Router's
basepathoption is currently only used for:But it does not filter which history events the router processes. A router with
basepath: '/app'will still try to process navigation to/,/settings,/other-module, etc.Solution
Add a basepath scope check at the beginning of the router's
loadmethod. When a router has abasepathconfigured, it now only processes location changes within its basepath scope.Implementation
New utility function
isPathInScope(pathname, basepath)inpath.ts:trueif the pathname is within the basepath scope/appdoesn't match/application)Basepath check in
router.load():basepathis set and not/, check if current path is in scopeHow React Router Handles This
This implementation mirrors React Router's
stripBasenamebehavior:When
stripBasename()returnsnull,matchRoutes()returnsnull, and nothing renders - the router completely ignores out-of-scope paths.Use Case: Micro-Frontends
Before This Fix
/user-management/users(MFE is loaded)/settings(shell route)/settingsdefaultNotFoundComponentrendersAfter This Fix
/user-management/users(MFE is loaded)/settings(shell route)/settingswithin/user-management? No/settingscorrectlyBreaking Changes
None for most users.
basepath(orbasepath: '/') behave exactly the samebasepathwill now correctly ignore out-of-scope pathsThis is actually fixing the expected behavior. If you set
basepath: '/app', you intuitively expect the router to only care about paths like/app/*.Testing
isPathInScope()://appvs/application)All existing tests pass (213 path tests total).
Files Changed
packages/router-core/src/path.ts- AddedisPathInScope()utilitypackages/router-core/src/router.ts- Added basepath scope check inload()packages/router-core/tests/path.test.ts- Added unit testsFixes #2103
Fixes #2108
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.