Aurelia Routing — Navigating Between Pages
In this tutorial, you will learn about Aurelia Routing. We cover key concepts, practical examples, and best practices to help you master this topic.
Aurelia's router maps URLs to components. It supports nested routes, route parameters, query strings, route guards, and programmatic navigation. The router is configured in the application shell and renders matched components in a <router-view> element.
What You'll Learn
You will learn how to configure routes, navigate between pages, use route parameters, implement route hooks, and build complex navigation structures.
Why It Matters
Routing is the backbone of multi-page applications. Aurelia's router provides URL-driven navigation with browser history support, making applications bookmarkable and navigable with the back button.
Real-World Use
An admin panel uses the router for section navigation (dashboard, users, settings, reports). Each section has nested routes for list, detail, and edit views. Route hooks protect admin routes from unauthorized access.
flowchart LR
A[Router Configuration] --> B[/dashboard]
A --> C[/users]
A --> D[/users/:id]
A --> E[/settings]
B --> F[Dashboard Component]
C --> G[User List]
D --> H[User Detail]
E --> I[Settings Form]
Basic Route Configuration
// src/app.ts
import { RouterConfiguration, Router } from 'aurelia-router';
import { PLATFORM } from 'aurelia-pal';
export class App {
router;
configureRouter(config, router) {
this.router = router;
config.title = 'My App';
config.options.pushState = true;
config.options.root = '/';
config.map([
{ route: '', name: 'home', moduleId: PLATFORM.moduleName('./home') },
{ route: 'about', name: 'about', moduleId: PLATFORM.moduleName('./about') },
{ route: 'contact', name: 'contact', moduleId: PLATFORM.moduleName('./contact') },
{ route: 'products', name: 'products', moduleId: PLATFORM.moduleName('./products') }
]);
}
}
<!-- src/app.html -->
<template>
<nav>
<a route-href="route: home">Home</a>
<a route-href="route: about">About</a>
<a route-href="route: contact">Contact</a>
<a route-href="route: products">Products</a>
</nav>
<router-view></router-view>
</template>
Route Parameters
config.map([
// Dynamic segments
{ route: 'products/:id', name: 'product-detail', moduleId: PLATFORM.moduleName('./product-detail') },
{ route: 'users/:username', name: 'user-profile', moduleId: PLATFORM.moduleName('./user-profile') },
{ route: 'categories/:slug/posts/:postId', name: 'post', moduleId: PLATFORM.moduleName('./post') },
// Optional parameters
{ route: 'search/:query?', name: 'search', moduleId: PLATFORM.moduleName('./search') },
// Wildcard route (404)
{ route: 'not-found', name: 'not-found', moduleId: PLATFORM.moduleName('./not-found') }
]);
// src/product-detail.ts
import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
@inject(Router)
export class ProductDetail {
constructor(router) {
this.router = router;
}
activate(params, routeConfig) {
this.productId = params.id;
console.log('Loading product:', params.id);
// Fetch product data using params.id
}
}
Navigation Methods
// Programmatic navigation
export class NavComponent {
constructor(router) {
this.router = router;
}
goHome() {
this.router.navigate('');
}
goToProduct(id) {
this.router.navigateToRoute('product-detail', { id: id });
}
goToSearch(query) {
this.router.navigateToRoute('search', { query: query });
}
goBack() {
this.router.navigateBack();
}
refresh() {
this.router.refresh();
}
redirectToLogin() {
this.router.navigate('login', { replace: true, trigger: true });
}
}
Navigation Hooks (Route Guards)
export class AdminRoute {
canActivate(params, routeConfig, navigationInstruction) {
// Check authentication
if (!this.auth.isAuthenticated) {
this.router.navigate('login');
return false; // Cancel navigation
}
if (!this.auth.isAdmin) {
this.router.navigate('forbidden');
return false;
}
return true; // Allow navigation
}
activate(params) {
// Route activated — load data
return this.api.getAdminData();
}
canDeactivate() {
// Confirm before leaving with unsaved changes
if (this.hasUnsavedChanges) {
return confirm('You have unsaved changes. Leave anyway?');
}
return true;
}
deactivate() {
// Clean up resources
this.dispose();
}
}
Child Routers
// src/admin.ts — Parent component with child router
export class Admin {
configureRouter(config, router) {
config.map([
{ route: '', name: 'dashboard', moduleId: PLATFORM.moduleName('./admin/dashboard') },
{ route: 'users', name: 'users', moduleId: PLATFORM.moduleName('./admin/users') },
{ route: 'users/:id', name: 'user-detail',moduleId: PLATFORM.moduleName('./admin/user-detail') },
{ route: 'settings', name: 'settings', moduleId: PLATFORM.moduleName('./admin/settings') }
]);
}
}
<!-- src/admin.html — Parent layout with child router -->
<template>
<div class="admin-layout">
<aside>
<a route-href="route: dashboard">Dashboard</a>
<a route-href="route: users">Users</a>
<a route-href="route: settings">Settings</a>
</aside>
<main>
<router-view></router-view>
</main>
</div>
</template>
Pipeline Steps
Customize the navigation pipeline with steps.
import { Redirect } from 'aurelia-router';
export class AuthStep {
run(navigationInstruction, next) {
if (navigationInstruction.getAllInstructions().some(i => {
return i.config.auth !== false;
})) {
// Check if user is authenticated
if (!isAuthenticated) {
return next.cancel(new Redirect('login'));
}
}
return next();
}
}
Register in config:
configureRouter(config, router) {
config.addPipelineStep('authorize', AuthStep);
config.map([...]);
}
Loading and Error States
<template>
<div class="page">
<!-- Loading indicator during route activation -->
<div if.bind="router.isNavigating" class="loading-bar">
Loading...
</div>
<!-- Error display -->
<div if.bind="error" class="error-message">
${error.message}
</div>
<!-- Route content -->
<router-view></router-view>
</div>
</template>
Hash vs PushState
// Hash-based URLs (default): #/products/123
config.options.pushState = false;
// HTML5 History API: /products/123 (requires server config)
config.options.pushState = true;
config.options.root = '/';
Common Mistakes
- Not using
PLATFORM.moduleNamefor module paths. Without it, routes break in production builds. - Forgetting
pushState: truerequires server configuration. PushState URLs cause 404 on page refresh without server fallback. - Not implementing
canActivatefor protected routes. Without route guards, protected pages are accessible to anyone. - Over-nesting routes. Keep hierarchy to 3 levels max. Deep nesting complicates navigation and data loading.
- Not handling the 404 route. Always include a catch-all route for unknown URLs.
Practice Questions
- How do you configure routes in Aurelia?
- How do you access route parameters in a component?
- What is the difference between
navigateandnavigateToRoute? - How do you protect routes with authentication?
- Challenge: Create an application with the following route structure:
/(home),/products(list),/products/:id(detail),/products/:id/edit(edit, protected),/admin(admin panel with child router),/auth/login(login),/*(404). Implement route guards for admin and edit routes. Include loading and error states.
FAQ
Mini Project
Build a documentation site with routes for sections and pages. Root router has top-level sections. Each section has a child router for pages within the section. Include: breadcrumb component, table of contents sidebar, search page with query param, 404 page, and loading indicators. Use canActivate to restrict admin pages.
What's Next
Now that you understand routing, learn Aurelia Router Lifecycle for navigation hooks. Then explore Aurelia Child Routers for nested navigation.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro