Skip to main content
Version: v9

ion-route

The route component takes a component and renders it when the Browser URL matches the url property.

note

Note: this component should only be used with vanilla and Stencil JavaScript projects. For Angular projects, use ion-router-outlet and the Angular router.

Catch-all Routes​

note

Catch-all routes are supported starting in Ionic v9.1. In earlier versions, url="/:any" can be used to catch paths whose first segment does not match another route. Unknown paths below a route that does match, such as /admin/settings, still render that route.

A url that ends in * is a catch-all route. It matches any remaining path, which is useful for showing a "not found" page. The part of the path matched by * is passed to the component as the pathMatch property, without a leading /.

<ion-router>
<ion-route url="/" component="page-home"></ion-route>
<ion-route url="/admin" component="page-admin"></ion-route>
<ion-route url="/admin/users/:id" component="page-admin-user"></ion-route>
<ion-route url="/admin/*" component="page-admin-not-found"></ion-route>
<ion-route url="*" component="page-not-found"></ion-route>
</ion-router>

With these routes:

PathRenderspathMatch
/adminpage-admin
/admin/users/1page-admin-user
/admin/settingspage-admin-not-foundsettings
/admin/users/1/editpage-admin-not-foundusers/1/edit
/settingspage-not-foundsettings

The component reads pathMatch like any other property:

customElements.define(
'page-not-found',
class extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<ion-content>
<p>No page found at /${this.pathMatch}</p>
</ion-content>
`;
}
}
);

Matching Priority​

Without a catch-all, a route that has a url and no child routes also matches paths that have extra segments after its url. Routes with an empty url, such as / or a default child route, only match exactly. When a catch-all route matches the path, routes that only match by ignoring extra segments are no longer considered, even if another route wins.

When a catch-all route and a route without a catch-all both match the whole path, the route with more fixed segments wins. A fixed segment is one that is not a parameter, such as admin. If they have the same number, the route without the catch-all wins.

Using the routes from the example above:

PathWithout catch-allsWith catch-allsWhy
/adminpage-adminpage-admin/admin and /admin/* both have one fixed segment, so the route without the catch-all wins.
/admin/users/1page-admin-userpage-admin-user/admin/users/:id has more fixed segments than /admin/*.
/admin/settingspage-adminpage-admin-not-foundpage-admin only matched by ignoring settings, so the catch-all wins.
/admin/users/1/editpage-admin-userpage-admin-not-foundpage-admin-user only matched by ignoring edit, so the catch-all wins.
/settingsNo matchpage-not-foundNo other route matches.

When more than one catch-all route matches, the one with the most fixed segments wins. Segments from parent routes count toward this total, so a catch-all route nested inside another route can handle unknown paths below it.

Redirects run before routes are matched. A redirect from * applies to every path, so <ion-route-redirect from="*"> takes precedence over <ion-route url="*">.

Caveats​

A catch-all route also matches its own prefix, so /admin/* matches /admin with pathMatch set to an empty string. This differs from ion-route-redirect, where from="/admin/*" does not match /admin.

A * is only treated as a wildcard when it is the last segment of the url. On a route with child routes, the * is ignored and the route matches as if its url did not end in *.

The router sets pathMatch on catch-all routes and overwrites any value passed in componentProps, so do not pass it there.

If the component of a catch-all route is a tab in ion-tabs, the tab does not receive pathMatch. Instead, nest the catch-all route as a child of the tab's route so it renders in that tab's ion-router-outlet:

<ion-route url="/docs" component="tab-docs">
<ion-route component="page-docs"></ion-route>
<ion-route url="*" component="page-docs-not-found"></ion-route>
</ion-route>

Navigation hooks can be used to perform tasks or act as navigation guards. Hooks are used by providing functions to the beforeEnter and beforeLeave properties on each ion-route. Returning true allows navigation to proceed, while returning false causes it to be cancelled. Returning an object of type NavigationHookOptions allows you to redirect navigation to another page.

Interfaces​

interface NavigationHookOptions {
/**
* A valid path to redirect navigation to.
*/
redirect: string;
}

Usage​

<ion-router>
<ion-route url="/home" component="page-home"></ion-route>
<ion-route url="/dashboard" component="page-dashboard"></ion-route>
<ion-route url="/new-message" component="page-new-message"></ion-route>
<ion-route url="/login" component="page-login"></ion-route>
</ion-router>
const dashboardPage = document.querySelector('ion-route[url="/dashboard"]');
dashboardPage.beforeEnter = isLoggedInGuard;

const newMessagePage = document.querySelector('ion-route[url="/dashboard"]');
newMessagePage.beforeLeave = hasUnsavedDataGuard;

const isLoggedInGuard = async () => {
const isLoggedIn = await UserData.isLoggedIn(); // Replace this with actual login validation

if (isLoggedIn) {
return true;
} else {
return { redirect: '/login' }; // If a user is not logged in, they will be redirected to the /login page
}
};

const hasUnsavedDataGuard = async () => {
const hasUnsavedData = await checkData(); // Replace this with actual validation

if (hasUnsavedData) {
return await confirmDiscardChanges();
} else {
return true;
}
};

const confirmDiscardChanges = async () => {
const alert = document.createElement('ion-alert');
alert.header = 'Discard Unsaved Changes?';
alert.message = 'Are you sure you want to leave? Any unsaved changed will be lost.';
alert.buttons = [
{
text: 'Cancel',
role: 'Cancel',
},
{
text: 'Discard',
role: 'destructive',
},
];

document.body.appendChild(alert);

await alert.present();

const { role } = await alert.onDidDismiss();

return role === 'Cancel' ? false : true;
};

Properties​

beforeEnter​

DescriptionA navigation hook that is fired when the route tries to enter. Returning true allows the navigation to proceed, while returning false causes it to be cancelled. Returning a NavigationHookOptions object causes the router to redirect to the path specified.
Attributeundefined
Type(() => NavigationHookResult | Promise<NavigationHookResult>) | undefined
Defaultundefined

beforeLeave​

DescriptionA navigation hook that is fired when the route tries to leave. Returning true allows the navigation to proceed, while returning false causes it to be cancelled. Returning a NavigationHookOptions object causes the router to redirect to the path specified.
Attributeundefined
Type(() => NavigationHookResult | Promise<NavigationHookResult>) | undefined
Defaultundefined

component​

DescriptionName of the component to load/select in the navigation outlet (ion-tabs, ion-router-outlet) when the route matches.

The value of this property is not always the tagname of the component to load, in ion-tabs it actually refers to the name of the ion-tab to select.
Attributecomponent
Typestring
Defaultundefined

componentProps​

DescriptionA key value { 'red': true, 'blue': 'white'} containing props that should be passed to the defined component when rendered.
Attributeundefined
Typeundefined | { [key: string]: any; }
Defaultundefined

url​

DescriptionRelative path that needs to match in order for this route to apply.

Accepts paths similar to expressjs so that you can define parameters in the url /foo/:bar where bar would be available in incoming props.
Attributeurl
Typestring
Default''

Events​

NameDescriptionBubbles
ionRouteDataChangedUsed internally by ion-router to know when this route did change.true

Methods​

No public methods available for this component.

CSS Shadow Parts​

No CSS shadow parts available for this component.

CSS Custom Properties​

No CSS custom properties available for this component.

Slots​

No slots available for this component.