Skip to content

Conversation

@ShaneK
Copy link
Member

@ShaneK ShaneK commented Dec 2, 2025

Issue number: resolves #24177


What is the current behavior?

Currently, Ionic Framework React Router only supports React Router 5. This has many issues and unsupported/broken features.

What is the new behavior?

With this change, Ionic Framework will support React Router 6 while still supporting transitions in the same way a native app does.

Most of what caused this change to take a long time is that React Router 5 and React Router 6 have fundamental differences in how they handle components once they're no longer part of the view. In this change, we move away from relying on React Router directly so much and have our own implementation for deciding how views get dealt with during navigation and when they're cleaned up, allowing for us to still transition between them like we need to while still using React Router as much as we possibly can.

This change will also lay the foundation for the migration to React Router 7, which will ideally be easier since most of the hard work has been dealt with here.

Does this introduce a breaking change?

  • Yes
  • No

Other information

Current dev build:

⚠️ WARNING: If you're going to use this dev build on an existing react project, you'll need to migrate to React Router 6. Migrating a large project at this point might be a bad idea since this will not release until v9, which will require further migrations and have other breaking changes! I do not currently have migration documentation for this, but it's a relatively easy process from the migrations I've been doing.

8.7.13-dev.11765921002.107104c2

thetaPC and others added 30 commits May 8, 2025 14:49
@ShaneK ShaneK mentioned this pull request Dec 3, 2025
3 tasks
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be beneficial to add to main now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this for personal usage?

Copy link
Member Author

@ShaneK ShaneK Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I also think it's good for everyone in the same way sync is. It does a lot of the annoying work of building and starting the server and killing the old one for you, which makes iteration much nicer. I 100% intend to commit this to main.

I'll probably create similar scripts for the other packages as we go.

* @param options The options for computing the parent path.
* @returns The computed parent path result.
*/
export const computeParentPath = (options: ComputeParentPathOptions): ParentPathResult => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can split some of the logic of computeParentPath to smaller functions so it's easier to digest the if statements.

Comment on lines +107 to +124
useEffect(() => {
const activeView = viewStack.current.findViewItemByRouteInfo(routeInfo, undefined, true);
const matchedParams = activeView?.routeData.match?.params as RouteParams | undefined;

if (matchedParams) {
const paramsCopy = filterUndefinedParams({ ...matchedParams });
if (areParamsEqual(routeInfo.params as RouteParams | undefined, paramsCopy)) {
return;
}
} else {
this.handleNavigate(pathname, 'push', 'none', undefined, routeOptions, tab);

const updatedRouteInfo: RouteInfo = {
...routeInfo,
params: paramsCopy,
};
locationHistory.current.update(updatedRouteInfo);
setRouteInfo(updatedRouteInfo);
}
}
}, [routeInfo]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a comment of what this is doing.

<IonHeader>
<IonToolbar>
<IonTitle>Main</IonTitle>
<IonTitle>Test App - React Router v{majorVersion}</IonTitle>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this change on main.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem necessary to port this to main when this is the first time we're updating the version.

Comment on lines 82 to 83
// cy.get('ion-menu.show-menu').should('exist');
// cy.wait(1000)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's get rid of the commented out code.

this.ionLifeCycleContext = context;
return (
<StackManager routeInfo={routeInfo}>
<StackManager routeInfo={routeInfo} id={id}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick

Suggested change
<StackManager routeInfo={routeInfo} id={id}>
<StackManager id={id} routeInfo={routeInfo}>

Comment on lines 95 to +96
setRef={(val: HTMLIonRouterOutletElement) => (this.ionRouterOutlet = val)}
id={id}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick

Suggested change
setRef={(val: HTMLIonRouterOutletElement) => (this.ionRouterOutlet = val)}
id={id}
id={id}
setRef={(val: HTMLIonRouterOutletElement) => (this.ionRouterOutlet = val)}


# Install Dependencies
npm install *.tgz --no-save
npm install *.tgz --no-save --legacy-peer-deps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a note of why legacy-peer-deps is needed and a TODO to remove it when possible.

…gation (#30854)

Issue number: resolves internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
When switching between tabs using the tab bar in a React Router 6
application, clicking IonBackButton incorrectly navigates back to the
previous tab. Each tab should have its own isolated navigation history
stack, and the back button should only navigate within the current tab's
history.

## What is the new behavior?

Tab navigation history is now properly isolated. When switching tabs via
the tab bar:
  - The back button only navigates within the current tab's history
- Pressing back on a tab root (with no navigation history) does nothing
instead of navigating to the previous tab or default route
- Within-tab navigation continues to work correctly (e.g., navigating to
a details page and back)
  - Tab history is preserved when switching away and returning to a tab

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Current dev build:
```
8.7.12-dev.11765377112.16762e5b
```
Copy link
Member

@brandyscarney brandyscarney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking really good! 🚀 My comments are mostly questions & potential issues with either the test app or the router code.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dynamic Routes

  1. Run the test app locally

  2. Navigate to Dynamic Routes

  3. Click on Take me to the newRoute

  4. 🐛 ❓ See that it navigates to a page that says New Route Failed:

    Image
  5. Go back

  6. Click Add Route

  7. Click on Take me to the newRoute

  8. 🐛 See that it changes the URL but the view doesn't update

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After merging all of my other RR changes into this branch (07104c2), I seem unable to replicate this. I believe this was fixed indirectly in the change that fixed changes during navigation, because it also fixed an issue with certain navigation directions. If you're able to, please pull and try this one again


For more information on migrating from React Router v5 to v6, refer to the [React Router v6 Upgrade Guide](https://reactrouter.com/en/main/upgrading/v5).

## Version 8.x
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version 8 needs to be moved to its own file, see the next branch: https://github.com/ionic-team/ionic-framework/blob/next/BREAKING_ARCHIVE/v8.md


<h2 id="version-9x-framework-specific">Framework Specific</h2>

<h4 id="version-9x-react">React</h4>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be React Router?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple Tabs

  1. Run the test app locally
  2. Navigate to Multiple Tabs
  3. Open the Menu and go to Tab 2
  4. Click Page D
  5. 🐛 See a flicker on the menu toggle button (note: this sometimes happens going from Page A to Page B also):
route-menu-bug.mov
  1. Refresh the page
  2. 🐛 See nothing loaded in the view

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested Outlet

Note

It is easier to see the transition issues in ios mode, so append ?ionic:mode=ios to the URL to switch for this test.

  1. Run the test app locally
  2. Navigate to Nested Outlet
  3. Open the console
  4. Click on Go to second page
  5. 🐛 See that there is no transition here
  6. Click on Back with direction "back"
  7. Click on Go to second page again
  8. 🐛 Repeat until you see a log when clicking Go to second page without navigation, see video below
nested-outlet-bug.mov

<IonHeader>
<IonToolbar>
<IonTitle>Main</IonTitle>
<IonTitle>Test App - React Router v{majorVersion}</IonTitle>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem necessary to port this to main when this is the first time we're updating the version.

cy.ionMenuNav('Home with redirect');
cy.ionPageVisible('home-page');
cy.ionPageDoesNotExist('favorites-page');
cy.ionPageHidden('favorites-page');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this change needed?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the reason for this update?

Comment on lines +141 to +143
let firstSpecificMatch: string | undefined = undefined;
let firstWildcardMatch: string | undefined = undefined;
let indexMatchAtMount: string | undefined = undefined;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let firstSpecificMatch: string | undefined = undefined;
let firstWildcardMatch: string | undefined = undefined;
let indexMatchAtMount: string | undefined = undefined;
let firstSpecificMatch: string | undefined;
let firstWildcardMatch: string | undefined;
let indexMatchAtMount: string | undefined;

Comment on lines +247 to +248
lastPathname: leavingLocationInfo.pathname, // The URL we just came from
pathname: location.pathname, // The current (destination) URL
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we change this to something like prevPathname and nextPathname to make it more obvious?

…RouterOutlet (#30844)

Issue number: resolves [an issue from a
comment](#24177 (comment))

---------

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

Currently, Ionic's RR6 doesn't support relative routes in the same way
RR6 does. Routes that do not start with a `/`do not work in the Ionic
RR6 implementation in some cases.

## What is the new behavior?

With this change, we properly support these route styles and more
closely align with normal RR6 route support.

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

Current dev build:
```
 8.7.12-dev.11765307927.1f491e92
```

This PR will be merged into the RR6 branch, which will be squash+merged
into the major 9 branch. This will prevent major 9 from having commits
in the change log on release that reference fixing things that are only
available in major 9.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

package: react @ionic/react package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants