Repository navigation
bug: angular: navigations that overlap a page transition can leave two pages visible #31650
Description
Ionic Framework Version
v9.x (9.0.9)
Current Behavior
When router navigations follow each other faster than the page transition — a navigation starts while the previous transition is still animating — the outlet sometimes settles with two .ion-page elements displayed: the page for the current URL and a page from an earlier navigation.
In a headless Chromium run of the reproduction below (iOS mode, default transitions), 5 of 40 bursts of 8 navigations, 30–330 ms apart, ended with two pages visible.
From reading StackController.wait() (common/directives/navigation/stack-controller.ts): every task waiting on runningTask resumes at the same moment once it settles, and promise.finally(() => (this.runningTask = undefined)) can clear the slot while a later task is still running, so queued transitions can run at the same time rather than one after another. That does not seem to be the only path: with wait() changed locally to chain tasks strictly, 1 of 40 bursts still failed.
Expected Behavior
Once navigations settle, exactly one page is visible: the one for the current URL.
Steps to Reproduce
- A standalone Angular app with
provideIonicAngular({ mode: 'ios' }), an<ion-router-outlet>and three sibling routesa,b,c, each a component with an<ion-content>. - Call
router.navigateByUrl()8 times in a row to random routes among the three, 30–330 ms apart. - Wait ~3 s and count the
ion-router-outlet > .ion-pageelements that are displayed with non-zero opacity.
const page = (name: string) => {
@Component({ selector: `page-${name}`, template: `<ion-content>${name}</ion-content>`, imports: [IonContent] })
class P {}
return P;
};
@Component({ selector: 'app-root', template: '<ion-app><ion-router-outlet /></ion-app>', imports: [IonApp, IonRouterOutlet] })
class Root {}
bootstrapApplication(Root, {
providers: [
provideZonelessChangeDetection(),
provideIonicAngular({ mode: 'ios' }),
provideRouter([
{ path: 'a', component: page('a') },
{ path: 'b', component: page('b') },
{ path: 'c', component: page('c') },
{ path: '', redirectTo: 'a', pathMatch: 'full' },
]),
],
});
// Driver: navigate 8 times to random routes, 30–330 ms apart, then after ~3 s:
const visible = [...document.querySelectorAll('ion-router-outlet > .ion-page')]
.filter((p) => getComputedStyle(p).display !== 'none' && +getComputedStyle(p).opacity > 0);
// Expected visible.length === 1; observed 2 in about 1 of 8 bursts.Code Reproduction URL
Inline above.
Ionic Info
@ionic/angular 9.0.9, @ionic/core 9.0.9, @angular/core 22.2.3 (zoneless), Chromium via Playwright.
Additional Information
In our app this showed up as tab switches settling on a page that was not the tab in the URL after fast taps.
Activity
thetaPC commentedon Oct 26, 2026
Thanks for the report!
Verified on 9.0.9. When a navigation reaches
ion-router-outletwhile an earlier page transition is still running, the outlet queues it behind that transition. Every queued navigation waits on the same transition, though, so when that transition ends they all start at once instead of one after another. Each navigation hides the other pages once its transition finishes, but it never hides the page it navigated to. When an older navigation finishes last, its page stays visible next to the page for the current URL. This happens in bothiosandmdmodes.Tracked as a bug in
@ionic/angular.