How to handle Ionic React Tabs routing with re-used page components across several tabs?

Hi all, I am using Ionic React Tabs for a music application. I have a quick question on how to handle an IonRouterOutlet for tabs that share the same page component across tabs. For example, my application has two pages:

  • Search Page
  • Feed Page

On both the Search and Feed pages, I need to allow a user to route to an Artist page. According to the documentation, I should be making my IonRouterOutlet look something like the following in my App.tsx:

<IonRouterOutlet>
    <Route path="/:tab(search)" component={Search} exact />
    <Route path="/:tab(search)/artist/:artistId" component={Artist} exact />
    <Route path="/:tab(feed)" component={Feed} exact />
    <Route path="/:tab(feed)/artist/:artistId" component={Artist} exact />
</IonRouterOutlet>

This is manageable so far, but now on each Artist page, a user can click an Album so I’d have to add another Route for both the Search and Feed tabs to account for the ability to route to /(whatever-tab)/album/:albumId. This is getting quickly out of hand because I have several shared components that are used amongst several tabs (4 tabs in my actual app). Is there a better way to handle this?

As mentioned in the docs, path simply uses regex to match the url. Is there a way to do something like:

<IonRouterOutlet>
    <Route path="/:tab(search)" component={Search} exact />
    <Route path="/:tab(feed)" component={Feed} exact />
    <!-- updated the following line --->
    <Route path="/:tab(search || feed)/artist/:artistId" component={Artist} exact />
</IonRouterOutlet>

So it can match several tabs based on what I pass as the current tab from the match props to my routerLink?

What’s the best practice here? The example tabs app doesn’t even use this :tab() syntax…is this out of date now? Thanks!

So you want the Artist page to show up under the search tab or the feed tab depending on where the user navigates from? Sounds like the Artist page doesn’t really have its own tab. Have you tried simply not setting one, i.e. mapping the page to /artist/:artistId?

In any case, Route is from React Router and path is documented there.

    <IonApp>
      <IonReactRouter>
        <IonTabs>
          <IonRouterOutlet>
            <Route path="/tab1" component={Tab1} exact={true} />
            <Route path="/commonTab/:tab" component={CommonTab} exact={true} />
            <Route path="/commonTab/details" component={Details} />
            <Route path="/tab4" component={Tab4} />
            <Route exact path="/" render={() => <Redirect to="/tab1" />} />
          </IonRouterOutlet>
          <IonTabBar slot="bottom" style={tabBarStyle}>
            <IonTabButton tab="tab1" href="/tab1">
              <IonIcon icon={flash} />
              <IonLabel>Tab One</IonLabel>
            </IonTabButton>
            <IonTabButton tab="tab2" href="/commonTab/tab2">
              <IonIcon icon={apps} />
              <IonLabel>Tab Two</IonLabel>
            </IonTabButton>
            <IonTabButton tab="tab3" href="/commonTab/tab3">
              <IonIcon icon={apps} />
              <IonLabel>Tab Three</IonLabel>
            </IonTabButton>
            <IonTabButton tab="tab4" href="/tab4">
              <IonIcon icon={send} />
              <IonLabel>Tab Four</IonLabel>
            </IonTabButton>
          </IonTabBar>
        </IonTabs>
      </IonReactRouter>
    </IonApp>
const CommonTab = () => {
  const history = useHistory();
  const params = useParams();

  console.log(params);
  return (
    <IonPage>
      <IonHeader>
        <IonToolbar>
          <IonTitle>{params.tab === "tab2" ? "Tab 2" : "Tab 3"}</IonTitle>
        </IonToolbar>
      </IonHeader>
      <IonContent>
        <IonList>
          <IonItem
            onClick={() => {
              history.push("commonTab/details");
            }}
          >
            <IonLabel>
              <h2>Go to detail</h2>
              <p>{JSON.stringify(params)}</p>
            </IonLabel>
          </IonItem>
        </IonList>
      </IonContent>
    </IonPage>
  );
};

export default CommonTab;
3 Likes

Hey @aaronksaunders I was actually looking at your Dev.to site last night and saw your comment about how :path had changed and to look at your newer projects for the updated method, but couldn’t find anything! I guess I didn’t convey my question properly. It looks like what you commented has to do with a shared base tab page? In my code I have certain page components that are used across several tabs after they press on a certain tab. For example:

User clicks on Search Tab -> they search for an artist -> they tap to go to Artist page
Now user clicks on Feed tab -> there is an artist in their feed -> they tap to go to Artist page

(Notice how the component that is the same is a subroute of the tab they’re in?)

Currently, I have my routing set up like this:

    {/* Feed */}
    <Route path="/:tab(feed)" component={Home} />
    <!-- Indentical to two routes in SEARCH except for :tab(search) prefix -->
    <Route path="/:tab(feed)/artist/:artistId" component={Artist} exact />
    <Route path="/:tab(feed)/album/:albumId" component={Album} exact />

    {/* Search */}
    <Route path="/:tab(search)" component={Search} exact />
    <!-- Indentical to two routes in FEED except for :tab(search) prefix -->
    <Route path="/:tab(search)/artist/:artistId" component={Artist} exact />
    <Route path="/:tab(search)/album/:albumId" component={Album} exact />

Am I interpreting what you posted incorrectly? It just feels odd having to duplicate code like this per-tab.

@mirkonasato you would be correct! Artist isn’t a tab at the bottom per-say. However the solution to just have a Route called artist/:artistId that looks something like this will present an issue:

<Route path="/artist/:artistId" component={Artist} exact />

The issue here is that if I’m on the Search tab, and I route to an Artist page using react-router-dom like this:

<Link to={`/artist/${artistId}`} />

This routes me to the Artist page while Search is still highlighted as the active tab at the bottom. However, if I were to tap over to a different tab (like the Feed tab) and then tap back to the Search tab. My current state is removed and I see the base Search page again instead of seeing the Artist on the Search page that I routed to.

Here’s a streamable that shows me doing the following: https://streamable.com/5o1zf0

  • Viewing the Search tab
  • searching for an artist and routing to artist/:artistId as you suggested
  • clicking over to Feed tab
  • routing back to Search and the artist I clicked on (Alison Wonderland) is not showing on that tab anymore

If I add corresponding :tab(search)/artist/:artistId or :tab(feed)/artist/:artistId then the tabs maintain their page state when clicking between them (which is desired)

Sorry I read the question wrong, but the same concept can be used to duplicate the detail page - see updated example - there is no duplicated code - https://stackblitz.com/edit/ionic-react-tabs-txjljk

I would strongly suggest you look in to using a better way to manage state than passing information around as url parameters, it will make it easier to test the components

@aaronksaunders thanks so far! This app will be deployed on web too, so passing the Artists Id seems logical since the url’s can be sharable and if a user pastes /artist/12 in to the browser url they should be routed to that specific artists’ page. What other methods do you suggest?

Interesting. You can pass an array with multiple values as the path if you want to.

Incidentally, React Router recommends using children elements these days rather than the component prop.

I think the solution I provided should solve your problem. I was just making a suggestion about the parameters; if it is web based, I think you are good

Thanks for that suggestion… will look into that approach also.

I like to emphasize readability and user simplicity in examples - lots of the people asking questions here, aren’t really advanced developers… they are just looking to get something working

Really its good I will try it. Thanks for your suggestion

This save my life!!!