Skip to content
Open
Show file tree
Hide file tree
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 May 8, 2025
f0127bd
refactor(utils): update matchPath for rr6
thetaPC May 8, 2025
cccf290
refactor(reactrouterviewstack): update for rr6 and improvements
thetaPC May 8, 2025
af7710b
refactor(test): updated test pages
thetaPC May 12, 2025
e364fea
chore(matchPath): use the correct library
thetaPC May 19, 2025
6a42e69
chore(rr6): run lint
thetaPC May 19, 2025
4aad76a
chore(StackManager): upgrade to rr6 and add comments
thetaPC May 19, 2025
fd6baac
chore(StackManager): more upgrades for rr6
thetaPC May 19, 2025
e76c1e8
docs(StackManager): update comment
thetaPC May 19, 2025
2436ba3
docs(matchPath): remove comment
thetaPC May 19, 2025
ca27ed6
chore(IonRouteInner): upgrade to rr6
thetaPC May 20, 2025
37f76c7
chore(StackManager): run lint
thetaPC Jun 9, 2025
a78f6b3
feat(IonRouter): migrate to functional component with react router 6
thetaPC Jun 9, 2025
93202c0
chore(IonRouteInner): update render
thetaPC Jun 9, 2025
0008059
docs(IonRouter, ReactRouterViewStack, StackManager): update comments
thetaPC Jun 9, 2025
0a0dcb6
feat(IonReactRouter): migrate to a functional component and react rou…
thetaPC Jun 11, 2025
331b394
feat(IonReactMemoryRouter): migrate to a functional component and rea…
thetaPC Jun 12, 2025
205a705
chore(IonReactMemoryRouter): remove unused type
thetaPC Jun 12, 2025
7ff8994
feat(IonReactHashRouter): migrate to a functional component and react…
thetaPC Jun 12, 2025
c45c0f9
docs(many): update comments
thetaPC Jun 13, 2025
12c49f5
refactor(many): remove `exact` from test pages
thetaPC Jun 13, 2025
71fb8cb
refactor(many): update to use <Navigate>
thetaPC Jun 13, 2025
c6f8dd4
refactor(many): replace `render` with `element`
thetaPC Jun 13, 2025
366004e
refactor(Tabs2): switch to navigate
thetaPC Jun 13, 2025
5cccf0b
refactor(many): update to prevent compile errors
thetaPC Jun 13, 2025
e6e17eb
refactor(IonReactRouter): split component to use hooks correctly
thetaPC Jun 13, 2025
2656e98
chore: update package and sync file
thetaPC Jun 13, 2025
10bb889
docs(IonReactRouter): use updated param names
thetaPC Jun 16, 2025
7a20697
fix(ReactRouterViewStack, StackManager): use correct value & add valu…
thetaPC Jun 17, 2025
9d69a69
Merge branch 'main' of github.com:ionic-team/ionic-framework into mh/…
thetaPC Jun 17, 2025
9c5f55a
test(rr5): keep the old pages
thetaPC Jun 18, 2025
49cbc46
test(dynamic-ionpage-classnames): use old version for rr5
thetaPC Jun 19, 2025
a926134
chore(rr5, rr6): update package
thetaPC Jun 20, 2025
a65886b
test(app, routing): use index
thetaPC Jun 21, 2025
51933fe
Merge branch 'main' of github.com:ionic-team/ionic-framework into mh/…
ShaneK Jul 11, 2025
43e546b
Merge branch 'main' of github.com:ionic-team/ionic-framework into mh/…
ShaneK Jul 16, 2025
fca5e6c
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Jul 17, 2025
80ae239
WIP
ShaneK Jul 21, 2025
c8cefd5
WIP
ShaneK Jul 24, 2025
b0c95d4
merge
ShaneK Jul 31, 2025
662fd70
WIP
ShaneK Aug 4, 2025
210bc16
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Aug 5, 2025
6f7631d
feat(react-router): have navigation in the routing page generally wor…
ShaneK Aug 6, 2025
6f72bbf
feat(react-router): adding support for route params
ShaneK Aug 11, 2025
0949a87
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Oct 8, 2025
8fd53b2
fix(react-router): addressing some migration issues with memory and h…
ShaneK Oct 8, 2025
0ceee27
fix(react-router): improving navigation stack reliability
ShaneK Oct 8, 2025
a6131ba
fix(react-router): improving matching with wildcard and parameter routes
ShaneK Oct 11, 2025
8035c82
fix(react-router): fixing redirect for test app so it uses replace ro…
ShaneK Oct 11, 2025
a9a1de2
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Oct 16, 2025
e293cc0
chore(test): adding text in refs test for people manually testing
ShaneK Oct 20, 2025
99436a3
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Oct 20, 2025
33fc844
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Oct 23, 2025
28ca3a7
fix(react-router): adding support for nested parameter access
ShaneK Oct 25, 2025
0d83b77
fix(react-router): skip IonPage wait for Navigate, adding documentati…
ShaneK Oct 26, 2025
054220d
trying to fix redirect
ShaneK Oct 28, 2025
1ed61a9
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Oct 30, 2025
8dddf77
fix(react-router): fixing dynamic tab and nested outlet tests so they…
ShaneK Oct 30, 2025
5b735cd
fix(router): correct relative base and param reuse
ShaneK Oct 30, 2025
881068e
Fixing test app tabs views
ShaneK Nov 7, 2025
6cae9ad
fix(react-router): trying to better handle nested routing
ShaneK Nov 20, 2025
763621d
fix(react-router): prevent unmounted Navigate components from trigger…
ShaneK Nov 21, 2025
d51f95e
fix(react-router): use state to track IonRouterOutlet ref id in test
ShaneK Nov 21, 2025
59f2dbf
fix(react): automatically dismiss inline overlays on navigation
ShaneK Nov 21, 2025
96c96fc
fix(react-router): add wildcard to refs route for nested routing
ShaneK Nov 21, 2025
383ec04
chore(status): updating status file
ShaneK Nov 22, 2025
10c31ed
chore(react-router): improving continuous testability
ShaneK Nov 24, 2025
045b0a7
fix(react-router): correct tab and nested outlet navigation
ShaneK Nov 24, 2025
3073a24
fix(react-router): fixing relative paths in tab context tests
ShaneK Nov 24, 2025
584dcf2
fix(react-router): prioritize specific route matches
ShaneK Nov 25, 2025
b02c197
fix(react-router): prevent incorrect view reuse for parameterized routes
ShaneK Nov 26, 2025
7fd0659
fix(react-router): nested redirect fix
ShaneK Nov 26, 2025
fc6e482
chore(react-router): cleaning up console logs
ShaneK Nov 26, 2025
6575a92
chore(react-router): removing rr5 test app since it will no longer be…
ShaneK Nov 26, 2025
fbed1e0
chore(react-router): fixing types, removing usued variable
ShaneK Nov 26, 2025
8fbd2d4
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Nov 26, 2025
3912623
fix(react-router): hide deactivated catch-all routes
ShaneK Nov 26, 2025
00798d4
fix(react-router): adding legacy peer dep flag during installation to…
ShaneK Nov 29, 2025
82fd1ba
fix(react-router): fixing views not being cleaned up properly, causin…
ShaneK Dec 1, 2025
5a77916
Merge branch 'main' of github.com:ionic-team/ionic-framework into sk/…
ShaneK Dec 1, 2025
397b6f7
chore(react-router): code clean up
ShaneK Dec 2, 2025
71e55ad
chore(react-router): refactor
ShaneK Dec 2, 2025
418ac75
chore(react-router): cleaning up util files
ShaneK Dec 2, 2025
99dcb35
chore(react-router): minor clean up
ShaneK Dec 2, 2025
a23f555
fix(react): fixing inline overlays being a bit eager to dismiss
ShaneK Dec 3, 2025
a565a37
fix(overlays): removing old band-aid code
ShaneK Dec 3, 2025
cb94f73
chore(docs): removing status doc link
ShaneK Dec 4, 2025
fedca46
fix(react-router): preserve nested outlet params when navigating betw…
ShaneK Dec 4, 2025
b2a7105
chore(react-router): adding migration details to BREAKING.md
ShaneK Dec 4, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ jobs:
strategy:
fail-fast: false
matrix:
apps: [reactrouter5]
apps: [reactrouter6]
needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stencil-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ jobs:
strategy:
fail-fast: false
matrix:
apps: [reactrouter5]
apps: [reactrouter6]
Copy link
Member

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.md

Copy link
Member Author

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.

Copy link
Member Author

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

needs: [build-react, build-react-router]
runs-on: ubuntu-latest
steps:
Expand Down
252 changes: 252 additions & 0 deletions docs/react-router/react-router-6-status.md
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 |
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading