iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Routing

Ionic uses framework-native routers (Angular Router, React Router, Vue Router) under an IonRouterOutlet for native-feeling stack transitions and back-gesture support.

Angular + React + Vue patterns

EXAMPLE
<!-- === Angular === -->

<!-- app-routing.module.ts -->
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
import { authGuard, adminGuard } from './guards';

const routes: Routes = [
    { path: '', redirectTo: 'home', pathMatch: 'full' },

    // Lazy-load by component
    { path: 'home',     loadComponent: () => import('./home/home.page').then(m => m.HomePage) },
    { path: 'login',    loadComponent: () => import('./login/login.page').then(m => m.LoginPage) },

    // Route params
    { path: 'post/:id', loadComponent: () => import('./post/post.page').then(m => m.PostPage) },

    // Guards
    { path: 'account',
      loadComponent: () => import('./account/account.page').then(m => m.AccountPage),
      canActivate: [authGuard],
    },
    { path: 'admin',
      loadChildren: () => import('./admin/admin.routes').then(m => m.routes),
      canActivate: [adminGuard],
    },

    // Tabs container
    { path: 'tabs',
      loadComponent: () => import('./tabs/tabs.page').then(m => m.TabsPage),
      children: [
          { path: '',        redirectTo: 'feed', pathMatch: 'full' },
          { path: 'feed',    loadComponent: () => import('./feed/feed.page').then(m => m.FeedPage) },
          { path: 'profile', loadComponent: () => import('./profile/profile.page').then(m => m.ProfilePage) },
      ],
    },

    // Catch-all
    { path: '**', loadComponent: () => import('./not-found/not-found.page').then(m => m.NotFoundPage) },
];

@@NgModule({
    imports: [RouterModule.forRoot(routes, {
        preloadingStrategy: PreloadAllModules,    // preload after navigation
        scrollPositionRestoration: 'enabled',
    })],
    exports: [RouterModule],
})
export class AppRoutingModule {}

<!-- app.component.html -->
<ion-app>
    <ion-router-outlet></ion-router-outlet>
</ion-app>

<!-- Navigate programmatically (Angular) -->
import { NavController } from '@ionic/angular';

constructor(private nav: NavController) {}

openPost(id: string) {
    this.nav.navigateForward(`/post/${id}`, { animated: true });
}
back() {
    this.nav.navigateBack('/home');
}
replaceWithRoot(url: string) {
    this.nav.navigateRoot(url);    // resets the stack
}

<!-- Read params -->
import { ActivatedRoute } from '@angular/router';
constructor(private route: ActivatedRoute) {
    this.id = this.route.snapshot.paramMap.get('id')!;
}
// Or subscribe to changes:
this.route.paramMap.subscribe(p => this.id = p.get('id'));

<!-- Query params -->
this.nav.navigateForward('/search', { queryParams: { q: 'docker' } });
this.route.snapshot.queryParamMap.get('q');

<!-- Lifecycle hooks (Ionic-specific) -->
ionViewWillEnter()   {}      // before page becomes active
ionViewDidEnter()    {}      // after page is shown
ionViewWillLeave()   {}      // before navigating away
ionViewDidLeave()    {}      // after navigated away

<!-- === React (Ionic React) === -->

import { IonReactRouter } from '@ionic/react-router';
import { IonApp, IonRouterOutlet, IonTabs, IonTabBar, IonTabButton, IonIcon, IonLabel } from '@ionic/react';
import { Route, Redirect } from 'react-router-dom';
import { home, person } from 'ionicons/icons';

export default function App() {
    return (
        <IonApp>
            <IonReactRouter>
                <IonRouterOutlet>
                    <Route exact path="/home"      component={HomePage} />
                    <Route       path="/post/:id"  component={PostPage} />
                    <Route       path="/tabs"      component={TabsPage} />
                    <Route exact path="/"          render={() => <Redirect to="/home" />} />
                    <Route                          component={NotFoundPage} />
                </IonRouterOutlet>
            </IonReactRouter>
        </IonApp>
    );
}

function TabsPage() {
    return (
        <IonTabs>
            <IonRouterOutlet>
                <Route exact path="/tabs/feed"    component={FeedPage} />
                <Route exact path="/tabs/profile" component={ProfilePage} />
                <Route exact path="/tabs"          render={() => <Redirect to="/tabs/feed" />} />
            </IonRouterOutlet>
            <IonTabBar slot="bottom">
                <IonTabButton tab="feed" href="/tabs/feed">
                    <IonIcon icon={home} /><IonLabel>Feed</IonLabel>
                </IonTabButton>
                <IonTabButton tab="profile" href="/tabs/profile">
                    <IonIcon icon={person} /><IonLabel>Profile</IonLabel>
                </IonTabButton>
            </IonTabBar>
        </IonTabs>
    );
}

import { useHistory, useParams } from 'react-router-dom';
function PostPage() {
    const history = useHistory();
    const { id } = useParams<{ id: string }>();
    return <IonButton onClick={() => history.push('/home')}>Back</IonButton>;
}

<!-- === Vue (Ionic Vue) === -->

import { createRouter, createWebHistory } from '@ionic/vue-router';
import HomePage from './pages/HomePage.vue';

const routes = [
    { path: '/',         redirect: '/home' },
    { path: '/home',     component: HomePage },
    { path: '/post/:id', component: () => import('./pages/PostPage.vue') },
    { path: '/tabs',     component: () => import('./pages/TabsPage.vue'),
        children: [
            { path: '',         redirect: 'feed' },
            { path: 'feed',     component: () => import('./pages/FeedPage.vue') },
            { path: 'profile',  component: () => import('./pages/ProfilePage.vue') },
        ],
    },
];

export const router = createRouter({
    history: createWebHistory(import.meta.env.BASE_URL),
    routes,
});

// main.ts
import { IonicVue } from '@ionic/vue';
import { createApp } from 'vue';
import App from './App.vue';
import { router } from './router';
const app = createApp(App).use(IonicVue).use(router);
router.isReady().then(() => app.mount('#app'));

// App.vue
<template>
    <ion-app>
        <ion-router-outlet />
    </ion-app>
</template>

// Navigate (Composition API)
import { useRouter, useRoute } from 'vue-router';
const router = useRouter();
const route = useRoute();

router.push(`/post/${id}`);
router.back();
router.replace('/home');

const id = computed(() => route.params.id);

<!-- === Common features === -->

<!-- Back button (auto-detects history) -->
<ion-back-button defaultHref="/home"></ion-back-button>

<!-- Modal that doesn't change URL -->
import { ModalController } from '@ionic/angular';
async presentModal() {
    const m = await this.modalCtrl.create({ component: EditPage });
    await m.present();
}

<!-- Deep linking (Capacitor + universal links) -->
<!-- iOS:    AASA (apple-app-site-association) file -->
<!-- Android: assetlinks.json + intent-filter in manifest -->
<!-- Web:    handled automatically by the router -->

<!-- Guards -->
import { CanActivateFn } from '@angular/router';
export const authGuard: CanActivateFn = (route, state) => {
    if (auth.isLoggedIn()) return true;
    return router.parseUrl(`/login?return=${state.url}`);
};

<!-- === Common bugs === -->
<!-- - Using <router-outlet> instead of <ion-router-outlet> → loses native gestures -->
<!-- - Tabs need IonTabs around BOTH IonTabBar and IonRouterOutlet -->
<!-- - Modal closes via dismiss(), not via router navigation -->
<!-- - Capacitor deep links require platform-specific config files -->
<!-- - On Android, hardware back button needs explicit handler in some cases -->

Why it matters

Use IonRouterOutlet instead of the framework’s default outlet — it’s what gives Ionic apps the native stack-swipe-back gesture and page transitions. Pair with guards + lazy loading for performance.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// React
import { IonReactRouter } from '@ionic/react-router';
<IonReactRouter>
    <IonRouterOutlet>
        <Route exact path="/home" component={Home} />
        <Route exact path="/profile/:id" component={Profile} />
    </IonRouterOutlet>
</IonReactRouter>
Try it Yourself »

Exercise

Route outlet element in Ionic React.

< >{routes}</ >

Test yourself

Q1. In Ionic React, the outlet element is…
Q2. Tabbed routes typically use…
Q3. Push navigation programmatically using…

Discussion

Loading…