Quick Reference ("How Do I...")

Navigate your Aurelia 2 application with confidence using this task-focused quick reference.

Table of Contents


Getting Started

How do I install and configure the router?

// Install
npm i @aurelia/router

// Configure in main.ts
import { RouterConfiguration } from '@aurelia/router';

Aurelia
  .register(RouterConfiguration.customize({
    useUrlFragmentHash: false,  // Clean URLs (default)
    historyStrategy: 'push',     // Browser history
  }))
  .app(MyApp)
  .start();

Full configuration options →

How do I define routes?

import { route } from '@aurelia/router';

@route({
  routes: [
    { path: '', component: Home, title: 'Home' },
    { path: 'about', component: About, title: 'About' },
    { path: 'users/:id', component: UserDetail }
  ]
})
export class MyApp {}

Configuring routes →

How do I set up a viewport?

<!-- In your root component template -->
<nav>
  <a href="home">Home</a>
  <a href="about">About</a>
</nav>

<au-viewport></au-viewport>

Viewports documentation →

How do I use hash-based routing instead of clean URLs?

RouterConfiguration.customize({
  useUrlFragmentHash: true  // URLs like /#/about
})

Hash vs PushState routing →


<!-- Using href (simple) -->
<a href="about">About</a>
<a href="users/42">User 42</a>

<!-- Using load (structured) -->
<a load="route: users; params.bind: {id: userId}">User Profile</a>

Navigation methods →

How do I navigate programmatically?

import { IRouter } from '@aurelia/router';
import { resolve } from '@aurelia/kernel';

export class MyComponent {
  private readonly router = resolve(IRouter);

  navigateToUser(id: number) {
    this.router.load(`users/${id}`);

    // Or with options
    this.router.load('users', {
      params: { id },
      queryParams: { tab: 'profile' }
    });
  }
}

Using the Router API →

// Configure active class globally
RouterConfiguration.customize({
  activeClass: 'active'
})
<!-- Use with load attribute -->
<a load="home" active.bind="isHomeActive">Home</a>

<!-- Or use the configured active class -->
<a load="home">Home</a>  <!-- Gets 'active' class automatically -->

Active CSS class →

How do I navigate to parent routes from nested components?

<!-- Using href with ../ prefix -->
<a href="../sibling">Go to sibling route</a>

<!-- Using load with context -->
<a load="route: sibling; context.bind: parentContext">Sibling</a>

Ancestor navigation →

How do I pass query parameters?

// Programmatically
router.load('search', {
  queryParams: { q: 'aurelia', page: 1 }
});
// Result: /search?q=aurelia&page=1

Query parameters →

Good news: External links work automatically! The router automatically ignores:

<!-- These automatically bypass the router (no special attributes needed!) -->
<a href="https://example.com">External site</a>
<a href="mailto:[email protected]">Email</a>
<a href="tel:+1234567890">Phone</a>
<a href="//cdn.example.com/file.pdf">Protocol-relative</a>
<a href="ftp://files.example.com">FTP</a>

<!-- Also bypassed: -->
<a href="/internal" target="_blank">New tab</a>
<a href="/internal" target="other">Named target</a>

Only use external attribute for edge cases:

<!-- When URL looks internal but should bypass router -->
<a href="/api/download" external>API endpoint</a>
<a href="/old-page.html" external>Legacy HTML page</a>

How it works: The router uses the URL constructor to check if a link is external. Any URL that can be parsed without a base (like https://, mailto:, etc.) is automatically treated as external.

Bypassing the router →


Route Parameters

How do I define route parameters?

@route({
  routes: [
    { path: 'users/:id', component: UserDetail },           // Required
    { path: 'posts/:id?', component: PostDetail },          // Optional
    { path: 'files/*path', component: FileViewer },         // Wildcard
    { path: 'items/:id{{^\\d+$}}', component: ItemDetail }, // Constrained
  ]
})

Path and parameters →

How do I access route parameters in my component?

import { IRouteViewModel, Params } from '@aurelia/router';

export class UserDetail implements IRouteViewModel {
  userId: string;

  canLoad(params: Params) {
    this.userId = params.id;
    return true;
  }
}

Lifecycle hooks →

How do I get all parameters including from parent routes?

import { IRouteContext } from '@aurelia/router';
import { resolve } from '@aurelia/kernel';

export class NestedComponent {
  private readonly routeContext = resolve(IRouteContext);

  attached() {
    const allParams = this.routeContext.getRouteParameters<{
      companyId: string;
      projectId: string;
      userId: string;
    }>({ includeQueryParams: true });
  }
}

Aggregate parameters → Route parameters guide →

How do I constrain parameters with regex?

{
  path: 'users/:id{{^\\d+$}}',  // Only numbers
  component: UserDetail
}

Constrained parameters →


Route Protection

How do I protect routes (authentication)?

import { lifecycleHooks } from '@aurelia/runtime-html';
import { IRouteViewModel, Params, RouteNode } from '@aurelia/router';

@lifecycleHooks()
export class AuthHook {
  canLoad(viewModel: IRouteViewModel, params: Params, next: RouteNode) {
    const isLoggedIn = !!localStorage.getItem('authToken');

    if (!isLoggedIn) {
      return 'login';  // Redirect to login
    }

    return true;  // Allow navigation
  }
}

Router hooks →

How do I implement authorization (role-based access)?

@lifecycleHooks()
export class AuthorizationHook {
  canLoad(viewModel: IRouteViewModel, params: Params, next: RouteNode) {
    const requiredPermission = next.data?.permission;

    if (requiredPermission && !this.hasPermission(requiredPermission)) {
      return 'forbidden';
    }

    return true;
  }

  private hasPermission(permission: string): boolean {
    // Check user permissions
    return true;
  }
}
// In route configuration
{
  path: 'admin',
  component: AdminPanel,
  data: { permission: 'admin' }
}

Router hooks example →

How do I prevent navigation away from unsaved forms?

import { IRouteViewModel, RouteNode } from '@aurelia/router';

export class EditForm implements IRouteViewModel {
  private isDirty = false;

  canUnload(next: RouteNode | null, current: RouteNode) {
    if (this.isDirty) {
      return confirm('You have unsaved changes. Leave anyway?');
    }
    return true;
  }
}

canUnload hook →

How do I redirect based on conditions?

export class Dashboard implements IRouteViewModel {
  canLoad(params: Params) {
    const userRole = this.authService.getRole();

    if (userRole === 'admin') {
      return 'admin/dashboard';
    } else if (userRole === 'user') {
      return 'user/dashboard';
    }

    return 'login';
  }
}

Redirect from canLoad →


Lifecycle Hooks

How do I load data before showing a component?

import { IRouteViewModel, Params } from '@aurelia/router';

export class UserDetail implements IRouteViewModel {
  user: User | null = null;

  async loading(params: Params) {
    this.user = await fetch(`/api/users/${params.id}`)
      .then(r => r.json());
  }
}

loading hook →

How do I run code after a component is fully loaded?

export class Dashboard implements IRouteViewModel {
  loaded(params: Params) {
    // Track page view
    analytics.track('page_view', { page: 'dashboard' });

    // Scroll to top
    window.scrollTo(0, 0);
  }
}

loaded hook →

When do lifecycle hooks run?

Hook
When
Use For

canLoad

Before activation

Guards, redirects, param validation

loading

After approval, before render

Data fetching, state setup

loaded

After render

Analytics, scroll, post-render effects

canUnload

Before deactivation

Unsaved changes warnings

unloading

Before removal

Cleanup, save drafts

Hook summary →

What's the difference between component hooks and router hooks?

  • Component hooks (IRouteViewModel): Implemented on the component itself

  • Router hooks (@lifecycleHooks()): Shared across multiple components

// Component hook
export class MyComponent implements IRouteViewModel {
  canLoad(params: Params) {
    // Runs only for this component
  }
}

// Router hook (shared)
@lifecycleHooks()
export class AuthHook {
  canLoad(viewModel: IRouteViewModel, params: Params) {
    // Runs for all components where this is registered
  }
}

Router hooks vs component hooks →


Advanced Topics

How do I handle 404 / unknown routes?

@route({
  routes: [
    { path: 'home', component: Home },
    { path: 'about', component: About },
    { path: 'not-found', component: NotFound }
  ],
  fallback: 'not-found'  // Redirect unknown routes here
})
export class MyApp {}

Fallback configuration →

How do I create route aliases / redirects?

@route({
  routes: [
    { path: '', redirectTo: 'home' },
    { path: 'about-us', redirectTo: 'about' },
    { path: 'home', component: Home },
    { path: 'about', component: About }
  ]
})

Redirects →

How do I work with multiple viewports (sibling routes)?

<au-viewport name="left"></au-viewport>
<au-viewport name="right"></au-viewport>
<!-- Load components into both viewports -->
<a href="products@left+details/42@right">Products + Details</a>
// Programmatically
router.load([
  { component: Products, viewport: 'left' },
  { component: Details, params: { id: 42 }, viewport: 'right' }
]);

Sibling viewports →

How do I implement nested/child routes?

@route({
  routes: [
    {
      path: 'users/:id',
      component: UserLayout,
      // Child routes defined in UserLayout
    }
  ]
})
export class MyApp {}

// In UserLayout
@route({
  routes: [
    { path: '', component: UserProfile },
    { path: 'posts', component: UserPosts },
    { path: 'settings', component: UserSettings }
  ]
})
export class UserLayout {}
<!-- UserLayout template -->
<h2>User: ${userId}</h2>
<nav>
  <a href="posts">Posts</a>
  <a href="settings">Settings</a>
</nav>
<au-viewport></au-viewport>

Hierarchical routing → Child routing playbook →

How do I lazy load routes?

@route({
  routes: [
    { path: 'home', component: Home },
    // Dynamic import for lazy loading
    { path: 'admin', component: () => import('./admin/admin-panel') }
  ]
})

Using inline import() →

How do I set/change the page title?

// In route configuration
{
  path: 'about',
  component: About,
  title: 'About Us'
}

// Programmatically
router.load('about', { title: 'Custom Title' });

// Custom title building
RouterConfiguration.customize({
  buildTitle(transition) {
    const titles = transition.routeTree.root.children.map(c => c.title);
    return `${titles.join(' - ')} | My App`;
  }
})

Setting titles → | Customizing titles →

How do I generate URLs without navigating?

import { IRouter } from '@aurelia/router';
import { resolve } from '@aurelia/kernel';

const router = resolve(IRouter);

// Generate path
const userPath = await router.generatePath({
  component: 'users',
  params: { id: 42 }
});
// Result: "/users/42"

// Use in template
<a href.bind="userPath">View User</a>

Path generation →

How do I work with base paths (multi-tenant apps)?

RouterConfiguration.customize({
  basePath: '/tenant1/app'  // All routes will be prefixed
})
<base href="/tenant1/app">

Base path configuration →

How do I handle browser back/forward buttons?

// The router handles this automatically with historyStrategy

// To control history behavior per navigation:
router.load('page', {
  historyStrategy: 'replace'  // Don't create history entry
});

router.load('page', {
  historyStrategy: 'push'  // Create history entry (default)
});

History strategy →

How do I access the current route information?

import { ICurrentRoute } from '@aurelia/router';
import { resolve } from '@aurelia/kernel';

export class MyComponent {
  private readonly currentRoute = resolve(ICurrentRoute);

  attached() {
    console.log('Current path:', this.currentRoute.path);
    console.log('Parameters:', this.currentRoute.parameterInformation);
  }
}

Current route →


Troubleshooting

My routes don't work with clean URLs (no hash)

Problem: Getting 404 errors when refreshing or accessing routes directly

Solution:

  1. Ensure <base href="/"> is in your HTML

  2. Configure server for SPA routing (return index.html for all routes)

  3. Or use hash routing: useUrlFragmentHash: true

PushState configuration →

Problem: External links somehow being handled by router

This should NOT happen - the router automatically ignores external links like https://, mailto:, tel:, etc.

If it's happening:

  1. Check your link format - is it truly external?

  2. You probably don't need the external attribute anymore

  3. Links with protocol (https://, mailto:) are automatically bypassed

Only needed for edge cases:

<!-- Internal-looking URLs that should bypass router -->
<a href="/api/download" external>API endpoint</a>
<a href="/static/old-page.html" external>Legacy page</a>

Bypassing href →

Problem: Links to sibling routes not working

Solution: Use ../ prefix for parent context

<a href="../sibling">Sibling Route</a>

Ancestor navigation →

My lifecycle hooks aren't being called

Problem: canLoad, loading, etc. not executing

Solution: Implement the IRouteViewModel interface

import { IRouteViewModel } from '@aurelia/router';

export class MyComponent implements IRouteViewModel {
  canLoad(params: Params) { /* ... */ }
}

Lifecycle hooks →

Route parameters aren't updating when navigating between same routes

Problem: Navigating from /users/1 to /users/2 doesn't update component

Solution: Configure transition plan

{
  path: 'users/:id',
  component: UserDetail,
  transitionPlan: 'invoke-lifecycles'  // Re-invoke hooks
}

Transition plans →

How do I debug routing issues?

import { IRouterEvents } from '@aurelia/router';
import { resolve } from '@aurelia/kernel';

export class MyApp {
  constructor() {
    const events = resolve(IRouterEvents);

    events.subscribe('au:router:navigation-start', (evt) => {
      console.log('Navigation started:', evt);
    });

    events.subscribe('au:router:navigation-end', (evt) => {
      console.log('Navigation ended:', evt);
    });

    events.subscribe('au:router:navigation-error', (evt) => {
      console.error('Navigation error:', evt);
    });
  }
}

Router events →


Complete Documentation

Last updated

Was this helpful?