-
Notifications
You must be signed in to change notification settings - Fork 13.4k
feat(react-router): upgrade to react router 6 #30831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ShaneK
wants to merge
89
commits into
major-9.0
Choose a base branch
from
sk/react-router-6
base: major-9.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+3,740
−1,740
Open
Changes from 86 commits
Commits
Show all changes
89 commits
Select commit
Hold shift + click to select a range
4262324
chore(deps): update react router to v6
thetaPC f0127bd
refactor(utils): update matchPath for rr6
thetaPC cccf290
refactor(reactrouterviewstack): update for rr6 and improvements
thetaPC af7710b
refactor(test): updated test pages
thetaPC e364fea
chore(matchPath): use the correct library
thetaPC 6a42e69
chore(rr6): run lint
thetaPC 4aad76a
chore(StackManager): upgrade to rr6 and add comments
thetaPC fd6baac
chore(StackManager): more upgrades for rr6
thetaPC e76c1e8
docs(StackManager): update comment
thetaPC 2436ba3
docs(matchPath): remove comment
thetaPC ca27ed6
chore(IonRouteInner): upgrade to rr6
thetaPC 37f76c7
chore(StackManager): run lint
thetaPC a78f6b3
feat(IonRouter): migrate to functional component with react router 6
thetaPC 93202c0
chore(IonRouteInner): update render
thetaPC 0008059
docs(IonRouter, ReactRouterViewStack, StackManager): update comments
thetaPC 0a0dcb6
feat(IonReactRouter): migrate to a functional component and react rou…
thetaPC 331b394
feat(IonReactMemoryRouter): migrate to a functional component and rea…
thetaPC 205a705
chore(IonReactMemoryRouter): remove unused type
thetaPC 7ff8994
feat(IonReactHashRouter): migrate to a functional component and react…
thetaPC c45c0f9
docs(many): update comments
thetaPC 12c49f5
refactor(many): remove `exact` from test pages
thetaPC 71fb8cb
refactor(many): update to use <Navigate>
thetaPC c6f8dd4
refactor(many): replace `render` with `element`
thetaPC 366004e
refactor(Tabs2): switch to navigate
thetaPC 5cccf0b
refactor(many): update to prevent compile errors
thetaPC e6e17eb
refactor(IonReactRouter): split component to use hooks correctly
thetaPC 2656e98
chore: update package and sync file
thetaPC 10bb889
docs(IonReactRouter): use updated param names
thetaPC 7a20697
fix(ReactRouterViewStack, StackManager): use correct value & add valu…
thetaPC 9d69a69
Merge branch 'main' of github.com:ionic-team/ionic-framework into mh/…
thetaPC 9c5f55a
test(rr5): keep the old pages
thetaPC 49cbc46
test(dynamic-ionpage-classnames): use old version for rr5
thetaPC a926134
chore(rr5, rr6): update package
thetaPC a65886b
test(app, routing): use index
thetaPC 51933fe
Merge branch 'main' of github.com:ionic-team/ionic-framework into mh/…
ShaneK 43e546b
Merge branch 'main' of github.com:ionic-team/ionic-framework into mh/…
ShaneK fca5e6c
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 80ae239
WIP
ShaneK c8cefd5
WIP
ShaneK b0c95d4
merge
ShaneK 662fd70
WIP
ShaneK 210bc16
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 6f7631d
feat(react-router): have navigation in the routing page generally wor…
ShaneK 6f72bbf
feat(react-router): adding support for route params
ShaneK 0949a87
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 8fd53b2
fix(react-router): addressing some migration issues with memory and h…
ShaneK 0ceee27
fix(react-router): improving navigation stack reliability
ShaneK a6131ba
fix(react-router): improving matching with wildcard and parameter routes
ShaneK 8035c82
fix(react-router): fixing redirect for test app so it uses replace ro…
ShaneK a9a1de2
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK e293cc0
chore(test): adding text in refs test for people manually testing
ShaneK 99436a3
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 33fc844
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 28ca3a7
fix(react-router): adding support for nested parameter access
ShaneK 0d83b77
fix(react-router): skip IonPage wait for Navigate, adding documentati…
ShaneK 054220d
trying to fix redirect
ShaneK 1ed61a9
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 8dddf77
fix(react-router): fixing dynamic tab and nested outlet tests so they…
ShaneK 5b735cd
fix(router): correct relative base and param reuse
ShaneK 881068e
Fixing test app tabs views
ShaneK 6cae9ad
fix(react-router): trying to better handle nested routing
ShaneK 763621d
fix(react-router): prevent unmounted Navigate components from trigger…
ShaneK d51f95e
fix(react-router): use state to track IonRouterOutlet ref id in test
ShaneK 59f2dbf
fix(react): automatically dismiss inline overlays on navigation
ShaneK 96c96fc
fix(react-router): add wildcard to refs route for nested routing
ShaneK 383ec04
chore(status): updating status file
ShaneK 10c31ed
chore(react-router): improving continuous testability
ShaneK 045b0a7
fix(react-router): correct tab and nested outlet navigation
ShaneK 3073a24
fix(react-router): fixing relative paths in tab context tests
ShaneK 584dcf2
fix(react-router): prioritize specific route matches
ShaneK b02c197
fix(react-router): prevent incorrect view reuse for parameterized routes
ShaneK 7fd0659
fix(react-router): nested redirect fix
ShaneK fc6e482
chore(react-router): cleaning up console logs
ShaneK 6575a92
chore(react-router): removing rr5 test app since it will no longer be…
ShaneK fbed1e0
chore(react-router): fixing types, removing usued variable
ShaneK 8fbd2d4
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 3912623
fix(react-router): hide deactivated catch-all routes
ShaneK 00798d4
fix(react-router): adding legacy peer dep flag during installation to…
ShaneK 82fd1ba
fix(react-router): fixing views not being cleaned up properly, causin…
ShaneK 5a77916
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK 397b6f7
chore(react-router): code clean up
ShaneK 71e55ad
chore(react-router): refactor
ShaneK 418ac75
chore(react-router): cleaning up util files
ShaneK 99dcb35
chore(react-router): minor clean up
ShaneK a23f555
fix(react): fixing inline overlays being a bit eager to dismiss
ShaneK a565a37
fix(overlays): removing old band-aid code
ShaneK cb94f73
chore(docs): removing status doc link
ShaneK fedca46
fix(react-router): preserve nested outlet params when navigating betw…
ShaneK b2a7105
chore(react-router): adding migration details to BREAKING.md
ShaneK File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
brandyscarney marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,252 @@ | ||
| # React Router 6 Migration | ||
|
|
||
| **Branch:** `sk/react-router-6` | ||
| **Design Docs:** [PR #305](https://github.com/ionic-team/ionic-framework-design-documents/pull/305) | ||
| **Last Updated:** December 2, 2025 | ||
|
|
||
| ## Overview | ||
|
|
||
| The `@ionic/react-router` package has been updated to support React Router 6. This migration replaces the React Router 5 integration with native RR6 APIs while preserving Ionic's navigation patterns, animations, and view lifecycle management. | ||
|
|
||
| All Cypress tests are passing. | ||
|
|
||
| | Metric | Count | | ||
| |--------|-------| | ||
| | Total tests | 77 | | ||
| | Passing | 77 | | ||
| | Failing | 0 | | ||
|
|
||
| ## What Changed | ||
|
|
||
| ### Package Dependencies | ||
|
|
||
| The `@ionic/react-router` package now requires React Router 6: | ||
|
|
||
| ```json | ||
| "peerDependencies": { | ||
| "react-router": ">=6.0.0", | ||
| "react-router-dom": ">=6.0.0" | ||
| } | ||
| ``` | ||
|
|
||
| The `history` package dependency was updated from v4 to v5 (which RR6 uses internally). | ||
|
|
||
| ### Core Components | ||
|
|
||
| **IonRouter** (`IonRouter.tsx`) was rewritten as a functional component using React hooks. It now uses `useLocation` and `useNavigate` from React Router 6 instead of the `withRouter` HOC and `history` object from v5. The component continues to manage `LocationHistory` and compute `RouteInfo` objects for Ionic's view stacks and transition directions. | ||
|
|
||
| **ReactRouterViewStack** (`ReactRouterViewStack.tsx`) was substantially expanded to handle RR6's matching semantics. Key changes include: | ||
| - Support for RR6's `PathMatch` objects and pattern matching | ||
| - Handling of index routes and wildcard routes (`*`) | ||
| - View identity tracking for parameterized routes (`/user/:id`) | ||
| - Proper computation of parent paths for nested outlets | ||
| - View cleanup for cross-navigation scenarios (e.g., navigating between different tab stacks) | ||
|
|
||
| **StackManager** (`StackManager.tsx`) was updated to work with the new view stack implementation. Changes include: | ||
| - Parent path derivation using RR6's route matching | ||
| - Improved handling of `Navigate` redirect components | ||
| - Better coordination of entering/leaving views during transitions | ||
| - Hiding of deactivated catch-all routes to prevent visual glitches | ||
|
|
||
| **IonRouteInner** (`IonRouteInner.tsx`) was simplified to work with RR6's element-based routing instead of the component/render prop pattern. | ||
|
|
||
| **Router Components** (`IonReactRouter.tsx`, `IonReactHashRouter.tsx`, `IonReactMemoryRouter.tsx`) were updated to use RR6's router components and hooks. | ||
|
|
||
| ### New Utilities | ||
|
|
||
| Five utility modules were added to support RR6's routing model: | ||
|
|
||
| | File | Purpose | | ||
| |------|---------| | ||
| | `computeParentPath.ts` | Computes common path prefixes and determines specific route matches for nested outlets | | ||
| | `pathMatching.ts` | Extended path matching using RR6's `matchPath` with support for index routes | | ||
| | `pathNormalization.ts` | Path string normalization (leading/trailing slashes) | | ||
| | `routeElements.ts` | Extracts Route children from Routes wrappers, detects Navigate elements | | ||
| | `viewItemUtils.ts` | Sorts views by route specificity for proper matching priority | | ||
|
|
||
| The old `matchPath.ts` utility was removed as its functionality is now handled by RR6's native matching. | ||
|
|
||
| ### @ionic/react Changes | ||
|
|
||
| Several changes were made to the `@ionic/react` package to support the migration: | ||
|
|
||
| - **IonRoute** (`IonRoute.tsx`): Updated to work with RR6's element-based routing | ||
| - **IonRouterOutlet** (`IonRouterOutlet.tsx`): Updated for RR6 compatibility | ||
| - **LocationHistory** (`LocationHistory.ts`): Enhanced to track navigation direction more accurately | ||
| - **RouteManagerContext** (`RouteManagerContext.ts`): Added `clearOutletViews` method for cross-navigation cleanup | ||
| - **ViewLifeCycleManager** (`ViewLifeCycleManager.tsx`): Added support for new lifecycle events | ||
| - **createInlineOverlayComponent** (`createInlineOverlayComponent.tsx`): New utility to automatically dismiss inline overlays on navigation | ||
|
|
||
| ### Test App Updates | ||
|
|
||
| All test pages in `packages/react-router/test/base/src/pages/` were updated to use RR6 syntax: | ||
| - `<Route path="/foo" component={Foo} />` became `<Route path="/foo" element={<Foo />} />` | ||
| - Nested routes now require trailing wildcards (`path="parent/*"`) when they contain child outlets | ||
| - `<Redirect to="..." />` became `<Navigate to="..." replace />` | ||
| - Route params accessed via `useParams()` instead of `props.match.params` | ||
| - Links use relative paths where appropriate | ||
|
|
||
| A new test page (`nested-params/NestedParams.tsx`) was added to test parameterized nested routing scenarios. | ||
|
|
||
| The `reactrouter5` test app was removed since the package no longer supports RR5. | ||
|
|
||
| ## Test Coverage | ||
|
|
||
| The Cypress test suite covers the following scenarios: | ||
|
|
||
| | Suite | Tests | Description | | ||
| |-------|-------|-------------| | ||
| | routing.cy.js | 29 | Core navigation, tabs, back button, redirects, params | | ||
| | nested-outlets.cy.js | 11 | Nested `IonRouterOutlet` behavior, back navigation | | ||
| | swipe-to-go-back.cy.js | 8 | Gesture navigation, abort handling, tab interactions | | ||
| | cross-route-navigation.cy.js | 7 | Navigation between different route contexts (tabs, outlets) | | ||
| | multiple-tabs.cy.js | 4 | Switching between different tab configurations | | ||
| | overlays.cy.js | 3 | Modal cleanup on navigation | | ||
| | dynamic-routes.cy.js | 3 | Adding routes at runtime | | ||
| | dynamic-tabs.cy.js | 3 | Adding tabs at runtime | | ||
| | tabs.cy.js | 2 | Basic tab navigation and history | | ||
| | tab-context.cy.js | 2 | Programmatic tab switching via context | | ||
| | refs.cy.js | 2 | Ref forwarding to Ionic components | | ||
| | dynamic-ionpage-classnames.cy.js | 1 | Dynamic class application to IonPage | | ||
| | outlet-ref.cy.js | 1 | Ref access to IonRouterOutlet | | ||
| | replace-actions.cy.js | 1 | History replacement behavior | | ||
|
|
||
| ### New Test Suite: Cross-Route Navigation | ||
|
|
||
| A new test suite (`cross-route-navigation.cy.js`) was added to verify proper view cleanup when navigating between different route contexts: | ||
| - Tab-to-non-tab navigation | ||
| - Non-tab-to-tab navigation | ||
| - Between different tab configurations | ||
| - Deep link scenarios | ||
|
|
||
| ## Known Limitations | ||
|
|
||
| ### Route Path Syntax | ||
|
|
||
| Nested outlets require parent routes to include a trailing wildcard: | ||
|
|
||
| ```tsx | ||
| // Correct | ||
| <Route path="parent/*" element={<Parent />} /> | ||
|
|
||
| // Incorrect - child routes won't match | ||
| <Route path="parent" element={<Parent />} /> | ||
| ``` | ||
|
|
||
| This aligns with React Router 6's nested routing semantics where child routes are matched relative to the parent's path. | ||
|
|
||
| ### Relative vs Absolute Paths | ||
|
|
||
| React Router 6 strongly favors relative paths for nested routing. While absolute paths still work, using relative paths in nested outlets is recommended: | ||
|
|
||
| ```tsx | ||
| // Inside a component at /tabs/home | ||
| <Link to="details">Details</Link> // Navigates to /tabs/home/details | ||
| <Link to="/tabs/home/details">Details</Link> // Also works, but less flexible | ||
| ``` | ||
|
|
||
| ## CI Integration | ||
|
|
||
| The GitHub Actions workflows have been updated to run the RR6 test app: | ||
| - `.github/workflows/build.yml` | ||
| - `.github/workflows/stencil-nightly.yml` | ||
|
|
||
| The matrix now uses `reactrouter6` instead of `reactrouter5`. | ||
|
|
||
| ## Architecture Reference | ||
|
|
||
| The data flow through the routing system: | ||
|
|
||
| ``` | ||
| Browser History Change | ||
| | | ||
| v | ||
| IonRouter (useLocation/useNavigate) | ||
| | | ||
| +-- Updates LocationHistory | ||
| +-- Computes RouteInfo (action, direction, params) | ||
| | | ||
| v | ||
| RouteManagerContext | ||
| | | ||
| v | ||
| StackManager (per IonRouterOutlet) | ||
| | | ||
| +-- Derives parent path from route children | ||
| +-- Matches routes using ReactRouterViewStack | ||
| +-- Determines entering/leaving views | ||
| +-- Clears stale views on cross-navigation | ||
| | | ||
| v | ||
| ion-router-outlet.commit() | ||
| | | ||
| v | ||
| Native Ionic Transition | ||
| ``` | ||
|
|
||
| The key insight is that Ionic intercepts React Router's navigation events and translates them into its own view management system, which enables native-feeling animations and gestures while still using React Router for URL management. | ||
|
|
||
| ## Next Steps | ||
|
|
||
| ### Before Release | ||
|
|
||
| 1. ~~**Run a TypeScript strict check** on the `@ionic/react-router` package~~ Complete | ||
| 2. ~~**All Cypress tests passing**~~ Complete (77/77) | ||
| 3. **Manual testing pass** through the test app to verify animations and gestures feel correct | ||
| 4. **Code review** of the implementation | ||
|
|
||
| ### Documentation | ||
|
|
||
| The following documentation should be prepared before public release: | ||
|
|
||
| 1. **Migration guide** covering: | ||
| - Route syntax changes (`component` to `element`, `Redirect` to `Navigate`) | ||
| - Nested route wildcard requirements | ||
| - Accessing route params with hooks (`useParams()`) | ||
| - Link syntax changes (relative paths) | ||
| - Any removed or deprecated APIs | ||
|
|
||
| 2. **Updated API reference** for: | ||
| - `IonReactRouter`, `IonReactHashRouter`, `IonReactMemoryRouter` | ||
| - `IonRouterOutlet` behavior with nested routes | ||
| - `routeOptions.unmount` and `LocationHistory` behavior | ||
|
|
||
| ## Related Files | ||
|
|
||
| Source code: | ||
| - `packages/react-router/src/ReactRouter/IonRouter.tsx` | ||
| - `packages/react-router/src/ReactRouter/ReactRouterViewStack.tsx` | ||
| - `packages/react-router/src/ReactRouter/StackManager.tsx` | ||
| - `packages/react-router/src/ReactRouter/IonRouteInner.tsx` | ||
| - `packages/react-router/src/ReactRouter/IonReactRouter.tsx` | ||
| - `packages/react-router/src/ReactRouter/IonReactHashRouter.tsx` | ||
| - `packages/react-router/src/ReactRouter/IonReactMemoryRouter.tsx` | ||
| - `packages/react-router/src/ReactRouter/utils/` (5 utility modules) | ||
|
|
||
| Test infrastructure: | ||
| - `packages/react-router/test/base/` (shared test code) | ||
| - `packages/react-router/test/apps/reactrouter6/` (RR6 config) | ||
| - `packages/react-router/scripts/test_runner.sh` (test automation) | ||
|
|
||
| Supporting changes in @ionic/react: | ||
| - `packages/react/src/components/IonRoute.tsx` | ||
| - `packages/react/src/components/IonRouterOutlet.tsx` | ||
| - `packages/react/src/components/createInlineOverlayComponent.tsx` | ||
| - `packages/react/src/routing/LocationHistory.ts` | ||
| - `packages/react/src/routing/RouteManagerContext.ts` | ||
| - `packages/react/src/routing/ViewLifeCycleManager.tsx` | ||
|
|
||
| ## Commit History | ||
|
|
||
| Key commits on this branch: | ||
|
|
||
| | Commit | Description | | ||
| |--------|-------------| | ||
| | `418ac75` | Cleaning up util files | | ||
| | `82fd1ba` | Fix views not being cleaned up properly, causing cross navigation issues | | ||
| | `3912623` | Hide deactivated catch-all routes | | ||
| | `7fd0659` | Nested redirect fix | | ||
| | `b02c197` | Prevent incorrect view reuse for parameterized routes | | ||
| | `584dcf2` | Prioritize specific route matches | | ||
| | `045b0a7` | Correct tab and nested outlet navigation | | ||
| | `59f2dbe` | Automatically dismiss inline overlays on navigation | |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should either update the breaking changes file to include no longer supporting react router 5 or create a follow-up task since doing so might conflict with
next: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING.mdThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I still need to document migration strategies and such. For the most part, it's a pretty easy migration (ignore the inline overlay stuff, that was all removed), but I just need to write it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I went ahead and did this in this branch, figuring it would be an easy enough merge conflict resolution. If you'd like to review in an easier to read way:
https://github.com/ionic-team/ionic-framework/blob/b2a71050344052a4858d0c8b6712f0d5bc9d7219/BREAKING.md