Skip to content

bug: angular: navigations that overlap a page transition can leave two pages visible #31650

Open

Description

@tzdevelopteam

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

  1. A standalone Angular app with provideIonicAngular({ mode: 'ios' }), an <ion-router-outlet> and three sibling routes a, b, c, each a component with an <ion-content>.
  2. Call router.navigateByUrl() 8 times in a row to random routes among the three, 30–330 ms apart.
  3. Wait ~3 s and count the ion-router-outlet > .ion-page elements 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

  1. thetaPC commented on Oct 26, 2026

    @thetaPC
    Contributor

    Thanks for the report!

    Verified on 9.0.9. When a navigation reaches ion-router-outlet while 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 both ios and md modes.

    Tracked as a bug in @ionic/angular.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions