Understanding Tabs In Ionic Framework

A very popular pattern on all mobile platforms is the tabbed interface. This interface is especially popular on iOS where it is encouraged by Apple’s design language over a side menu. It has the advantage that the user can see all of the options available to them whereas a side menu’s options are hidden by design.

Ionic has a very simple tabs directive allowing you to implement these kind of interfaces with lots of options for different styles (icons only, text only, text and icon). In this tutorial, we’ll take a look at the ionic-starter-tabs project on GitHub and break it down piece by piece to get a good understanding of how it works.

Tabs Directives

The ion-tabs directive is made up of a few parts. First, the main directive that wraps everything related to the tabbed interface.


On this main tag, we can control the look and style by assigning CSS classes. For example, to use the positive color scheme and have tabs with only icons, we can assign the tabs-positive class and the tabs-icon-only class.

<ion-tabs class="tabs-positive tabs-icon-only">

A full list of tab-related classes can be found on the Ionic Docs.

Inside of the ion-tabs directive, we define each of our tabs and their content.


 <ion-tab title="Home">
 <!-- Tab 1 content -->

 <ion-tab title="About">
 <!-- Tab 2 content -->

 <ion-tab title="Settings">
 <!-- Tab 3 content -->


For each of these tabs we have defined their title, which will be used as the text (Assuming we have set up our tabs to show text). We also have the ability to specify an icon using the icon attribute passing in the name of an ionicons class or specify different icons for a tabs selected and not selected states (active and not active).


 <ion-tab title="Home" icon-on="ion-ios7-filing" icon-off="ion-ios7-filing-outline">
 <!-- Tab 1 content -->

 <ion-tab title="Settings" icon="ion-ios7-gear">
 <!-- Tab 3 content -->


You may also specify a badge (a little number of the icon like when you have notifications in an app), a function to call when a tab is selected, a URL to go to when a tab is selected, or override what happens when a tab is clicked. For a full list of attributes, see the Official Documentation.

Tabs Index

Let’s begin digging into the tabs starter project. The project is made up of the main index.html page, a tabs.html template which serves as our template and contains our tabs code, as well as templates for each view. Additionally, we have an app.js for our app’s configuration and controllers.js for our controller code.

The idea here is our index.html defines our app and creates kind of a shell. The tabs.html defines the structure of our app and inside of this structure is where the views live. Let’s take a look at our index.html page:

<!DOCTYPE html>
 <meta charset="utf-8">
 <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">

 <link href="lib/ionic/css/ionic.css" rel="stylesheet">
 <link href="css/style.css" rel="stylesheet">

 <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
 <link href="css/ionic.app.css" rel="stylesheet">

 <!-- ionic/angularjs js -->
 <script src="lib/ionic/js/ionic.bundle.js"></script>

 <!-- cordova script (this will be a 404 during development) -->
 <script src="cordova.js"></script>

 <!-- your app's js -->
 <script src="js/app.js"></script>
 <script src="js/controllers.js"></script>
 <script src="js/services.js"></script>
 <body ng-app="starter" animation="slide-left-right-ios7">
 The nav bar that will be updated as we navigate between views.
 <ion-nav-bar class="bar-stable nav-title-slide-ios7">
 <ion-nav-back-button class="button-icon icon ion-ios7-arrow-back">
 The views will be rendered in the <ion-nav-view> directive below
 Templates are in the /templates folder (but you could also
 have templates inline in this html file if you'd like).

You’ll notice how little there actually is here. As stated above, the idea here is that we have a shell for our app by marking the body with the ng-app attribute. In the case of this starter project, we have opted to place the ion-nav-bar in our index.html, but we could easy have placed it in our tabs.html page.


Let’s take a look at the tabs.html file. Remember, this file is our main template and will define both the tabs and where the content will fit in.

<ion-tabs class="tabs-icon-top">

 <!-- Dashboard Tab -->
 <ion-tab title="Dashboard" icon="icon ion-home" href="#/tab/dash">
 <ion-nav-view name="tab-dash"></ion-nav-view>

 <!-- Friends Tab -->
 <ion-tab title="Friends" icon="icon ion-heart" href="#/tab/friends">
 <ion-nav-view name="tab-friends"></ion-nav-view>

 <!-- Account Tab -->
 <ion-tab title="Account" icon="icon ion-gear-b" href="#/tab/account">
 <ion-nav-view name="tab-account"></ion-nav-view>


You’ll see we’ve defined our ion-tabs and ion-tab pattern.


Each ion-tab above has a ion-nav-view element inside of it. The ion-nav-view is where our child states (the views of our app) will be inserted and live. Each one of our tabs will have it’s own children and it’s own navigation history. Note that we have given each ion-nav-view a name attribute. This name will be used to reference to the ion-nav-view when we are building our states in the app’s config.

You’ll also notice each ion-tab has a href attribute pointing to a URL. This is the URL your app will navigate to when that tab is pressed.

Child View

Now that we have our app’s structure and main template, let’s take a look at what the child views (the actual content views of the app) look like.

<ion-view title="Dashboard">
 <ion-content class="padding">

This is tab-dash.html, the default view in our app. Everything in these template files should be wrapped in the ion-view tag, and the actual content for the view in a ion-content tag.


For this app, we have a controller for each child view that needs a controller. These are pretty straight forward as far as Ionic/Angular controllers go, so we won’t go much into that.


Here’s where the magic happens. Using the Angular-UI router, we want to define our tabs.html as an abstract state. From the Angular-UI docs an abstract state “…can have child states but can not get activated itself. An ‘abstract’ state is simply a state that can’t be transitioned to. It is activated implicitly when one of its descendants are activated.”. This is exactly what we want. We want our user to navigate to the actual views, but to inherit the main layout from the parent abstract state.

.config(function($stateProvider, $urlRouterProvider) {


 // setup an abstract state for the tabs directive
 .state('tab', {
 url: "/tab",
 abstract: true,
 templateUrl: "templates/tabs.html"

 // Each tab has its own nav history stack:

 .state('tab.dash', {
 url: '/dash',
 views: {
 'tab-dash': {
 templateUrl: 'templates/tab-dash.html',
 controller: 'DashCtrl'

 .state('tab.friends', {
 url: '/friends',
 views: {
 'tab-friends': {
 templateUrl: 'templates/tab-friends.html',
 controller: 'FriendsCtrl'
 .state('tab.friend-detail', {
 url: '/friend/:friendId',
 views: {
 'tab-friends': {
 templateUrl: 'templates/friend-detail.html',
 controller: 'FriendDetailCtrl'

 .state('tab.account', {
 url: '/account',
 views: {
 'tab-account': {
 templateUrl: 'templates/tab-account.html',
 controller: 'AccountCtrl'

 // if none of the above states are matched, use this as the fallback


Our base URL, as configured here, is /tab. Notice our child state uses dot notation where to the left of the dot is the name of the parent state and to the right of the dot is the name of the child state. In our url, we don’t need to tell the router to go to “/app/playlists”, as it already knows to do this.

Remember the name attributes on the ion-nav-view elements in the tabs.html page?

<ion-nav-view name="tab-dash"></ion-nav-view>

Here is the router, on each child state, we are defining that it should put it’s content (using the template file and the controller specified) in that ion-nav-view.


While there are a couple of concepts to wrap your head around when working with the tabs pattern, it becomes much easier to understand when you break it down piece by piece into bit sized pieces. Need further clarification or have a request for another Ionic post? Feel free to comment below or follow and mention me on twitter (@andrewmcgivery). You can also catch more blog posts by me on my blog.

Andrew McGivery

Andrew McGivery

Andrew McGivery works as a full time as a Web/Portal Developer in the System Integration and Innovation department at Conestoga College in Canada where the focus is on Research and Development for new projects, concepts, and technologies. Andrew runs a blog that covers many topics including the Ionic Framework.


Follow Us


Subscribe to my newsletter for monthly tips and tricks on subjects such as mobile, web, and game development.

Subscribe on YouTube

Support This Site