App Tabs

It's common for mobile apps to come with tabs at the bottom of the screen. These tabs act as separate routing stacks, so should remember where they were when a user navigates away and back to a tab.

Ionic provides several components to provide App Tabs out of the box, with a deep integration with the Ionic Router for them.

Read more about Ionic's ion-tabs here.

Setup an application with tabs

See the example in the playground for a working demo of an app with tabs.

Tabs require a main tab component, and then child components to be rendered in the tab view.

Your file structure should look like this:

pages/ directory
pages/
--| tabs.vue
--| tabs/
----| tab1.vue
----| tab2.vue
----| tab3.vue
----| tab4.vue

These tabs should then have a similar code structure as shown below. Remember, all pages must start with an ion-page component.

pages/tabs.vue
<template>
  <ion-page>
    <ion-content>
      <ion-tabs>
        <ion-router-outlet />
        
        <ion-tab-bar slot="bottom">
          <ion-tab-button tab="tab1" href="/tabs/tab1">
            <ion-icon :icon="ioniconsHomeOutline" />
            <ion-label>Tab 1</ion-label>
          </ion-tab-button>

          <ion-tab-button tab="tab2" href="/tabs/tab2">
            <ion-icon :icon="ioniconsImagesOutline" />
            <ion-label>Photos</ion-label>
          </ion-tab-button>

          <ion-tab-button tab="tab3" href="/tabs/tab3">
            <ion-icon :icon="ioniconsBulbOutline" />
            <ion-label>Tab 3</ion-label>
          </ion-tab-button>

          <ion-tab-button tab="tab4" href="/tabs/tab4">
            <ion-icon :icon="ioniconsAccessibilityOutline" />
            <ion-label>Animation examples</ion-label>
          </ion-tab-button>
        </ion-tab-bar>
      </ion-tabs>
    </ion-content>
  </ion-page>
</template>
pages/tabs/tab1.vue
<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Tab 1</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      Tab 1 content
    </ion-content>
  </ion-page>
</template>
pages/tabs/tab2.vue
<template>
  <ion-page>
    <ion-header>
      <ion-toolbar>
        <ion-title>Tab 2</ion-title>
      </ion-toolbar>
    </ion-header>
    <ion-content>
      Tab 2 content
    </ion-content>
  </ion-page>
</template>

All pages that should show inside these tabs must be sibling routes of these initial tab views, but with the name of the tab in its prefix.

The simplest way to manage this is for all pages being within this pages/tabs/ directory, with a directory per tab, like so:

pages/ directory
pages/
--| tabs.vue
--| tabs/
----| tab1/
------| index.vue
------| a-page.vue
----| tab2/
------| index.vue
----| tab3/
------| index.vue
----| tab4.vue
------| index.vue
------| another-page.vue

If a particular tab only has one route component, you don't explicitly need the directory for it with the index.vue inside of it, but we find it's a neater approach this way.

Using these directories avoids the routes becoming children routes of the tab by accident. The following folder structure is incorrect and would result in children routes, which IonRouter will not serve correctly:

  • An example of incorrect routing (do not copy):
pages/ directory
pages/
--| tabs.vue
--| tabs/
----| tab1.vue
----| tab1/
------| a-page.vue
----| tab2.vue
----| tab3.vue
----| tab4.vue
----| tab4/
------| another-page.vue

Routing to pages that shouldn't be displayed in the tabs

If you'd like to surface a page on-top of the tabs, rather than inside one of them, you can include the page component outside of the tabs directory.

pages/ directory
pages/
--| tabs.vue
--| tabs/
----| tab1/
------| index.vue
------| a-page.vue
----| tab2/
------| index.vue
--| settings.vue
  • When navigating from a tabbed route to a non-tabbed route, route.params from the auto-imported useRoute() will always be an empty object. A current workaround is to import { useRoute } from 'vue-router';.

Reusing views across tabs

Some apps may require showing the same component in different tabs. For instance, Spotify will allow you to view an album from both the Home and the Search tab.

To best accomplish this with Nuxt's file-based routing, create the routes using vue components in pages/tabs, and have them include the same component.

pages/ directory
pages/
--| tabs.vue
--| tabs/
----| home/
------| index.vue
------| album-[id].vue
----| search/
------| index.vue
------| album.vue
----| library/
------| index.vue
pages/home/album-{id}.vue
<template>
  <SingleAlbumView />
</template>
pages/search/album-{id}.vue
<template>
  <SingleAlbumView />
</template>