Ionic/react - nested routes

I am trying to nest routes at different levels of my React component tree but it seems that wrapping lower-level routes with the component causes them not to render (removing IonRouterOutlet fixes the functionality but I get no Ionic animations).

With the code below, clicking the “Page 1” link will update the URL but the page will not actually change. If you change the in Page1Router to a

Page 1 will then show when expected, but there is no animation when clicking the “Page A” link, since the router no longer has the .

Hopefully someone can just tell me I’m making a stupid mistake.

const App: React.FC = () => (
  <IonApp>
    <IonReactRouter>
      <IonRouterOutlet>
        <Route path="/" component={HomeRouter} />
      </IonRouterOutlet>
    </IonReactRouter>
  </IonApp>
);


const HomeRouter: React.FC = () => {
  return (
    <IonRouterOutlet>
      <Route path="/" component={Home} exact />
      <Route path="/page1" component={Page1Router} />
      <Route path="/page2" component={Page2} exact />
    </IonRouterOutlet>
  );
};


const Home: React.FC = () => {
  return (
    <IonPage>
      <IonContent>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">HOME PAGE</IonTitle>
          </IonToolbar>
        </IonHeader>

        <IonList>
          <IonItem routerLink="/page1">
            <IonLabel>Page 1</IonLabel>
          </IonItem>
          <IonItem routerLink="/page2">
            <IonLabel>Page 2</IonLabel>
          </IonItem>
        </IonList>

      </IonContent>
    </IonPage>
  );
};


const Page1Router: React.FC = () => {
  return (
    <IonRouterOutlet>
      <Route path="/page1" component={Page1} exact />
      <Route path="/page1/a" component={PageA} exact />
      <Route path="/page1/b" component={PageB} exact />
    </IonRouterOutlet>
  );
};


const Page1: React.FC = () => {
  return (
    <IonPage>
      <IonContent>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">Page 1</IonTitle>
          </IonToolbar>
        </IonHeader>

        <IonList>
            <IonItem routerLink="/page1/a">
              <IonLabel>Page A</IonLabel>
            </IonItem>
            <IonItem routerLink="/page1/b">
              <IonLabel>Page B</IonLabel>
            </IonItem>
          </IonList>
      </IonContent>
    </IonPage>
  );
};


const Page2: React.FC = () => {
  return (
    <IonPage>
      <IonContent>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">Page 2</IonTitle>
          </IonToolbar>
        </IonHeader>

        <h1>Page 2</h1>
      </IonContent>
    </IonPage>
  );
};


const PageA: React.FC = () => {
  return (
    <IonPage>
      <IonContent>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">PAGE A</IonTitle>
          </IonToolbar>
        </IonHeader>

        <h1>Page A</h1>

      </IonContent>
    </IonPage>
  );
};


const PageB: React.FC = () => {
  return (
    <IonPage>
      <IonContent>
        <IonHeader collapse="condense">
          <IonToolbar>
            <IonTitle size="large">PAGE B</IonTitle>
          </IonToolbar>
        </IonHeader>

        <h1>Page B</h1>

      </IonContent>
    </IonPage>
  );
};

I think the problem comes from mixing regular pages and a nested IonRouterOutlet inside the same IonRouterOutlet. Not sure if that’s by design or a bug (you could try raising an issue), but if you re-arrange your routes to have “leaf” outlets with only pages that will probably fix it. E.g.

    <IonRouterOutlet>
      <Route path="/" component={Home} exact />
      <Route path="/page1" component={Page1} exact />
      <Route path="/page1/a" component={PageA} exact />
      <Route path="/page1/b" component={PageB} exact />
      <Route path="/page2" component={Page2} exact />
    </IonRouterOutlet>

Incidentally, you don’t have to use IonRouterOutlet everywhere, only where you want transition effects for the pages inside it. So e.g. in the App component you could use a regular React Router Switch instead.

Just a simple musing here…

Simple is better.
Don’t try to over-complicate your app by nesting routes in other routes.
This is just asking for a headache

Thanks @mirkonasato for the reply. This was just the example code you get when you start a new blank Ionic project so that’s why the top level IonRouterOutlet was there on App. I agree that it doesn’t need to be there for this case.

As for flattening all our my routes into one file, this is not ideal for our app, which we originally built in Ionic 1 on Cordova, then migrated to Ionic 3, and are now migrating to the latest and switching to React. Our application currently has more than 60 distinct pages the majority of which will need its own route and transition animations.

@mhartington thanks for the thoughts. However, as the React Router docs state, nested routes help when code splitting which is a big reason we are upgrading our Ionic app to the latest and greatest. And, as I pointed out in my previous comment, our app is quite large and we have a lot invested into Ionic+Cordova.

Fair enough. Like I mentioned, you can try raising an issue.

As far as I can see mixing regular pages at the same level as a nested IonRouterOutlet just doesn’t work. If you inspect the elements it’s easy to see why. Normally when you navigate from one page to another inside the same outlet you see

Screenshot from 2020-08-08 20-15-30

so the second page is displayed on top because it has a larger z-index. But when the second page is inside a nested outlet you end up with

Screenshot from 2020-08-08 20-16-21

so the first page is still on top.

How about grouping your routes into separate files but without nested outlets? You could use React fragments, something like

const Page1Routes: React.FC = () => {
  return (
    <>
      <Route path="/page1" component={Page1} exact />
      <Route path="/page1/a" component={PageA} exact />
      <Route path="/page1/b" component={PageB} exact />
    </>
  );
};

then include those routes at the top level

    <IonRouterOutlet>
      <Route path="/" component={Home} exact />
      <Page1Routes />
    </IonRouterOutlet>

Since your example is not doing any code-spliting, you’re not going to get any benefits here.

But if you want to provide a more complete example…Then we can look at that instead of pseudo code.