LogoLogo
HomeDiscourseBlogDiscord
  • Introduction
  • Introduction
    • Quick start
    • Aurelia for new developers
    • Hello world
      • Creating your first app
      • Your first component - part 1: the view model
      • Your first component - part 2: the view
      • Running our app
      • Next steps
  • Templates
    • Template Syntax
      • Attribute binding
      • Event binding
      • Text interpolation
      • Template promises
      • Template references
      • Template variables
      • Globals
    • Custom attributes
    • Value converters (pipes)
    • Binding behaviors
    • Form Inputs
    • CSS classes and styling
    • Conditional Rendering
    • List Rendering
    • Lambda Expressions
    • Local templates (inline templates)
    • SVG
  • Components
    • Component basics
    • Component lifecycles
    • Bindable properties
    • Styling components
    • Slotted content
    • Scope and context
    • CustomElement API
    • Template compilation
      • processContent
      • Extending templating syntax
      • Modifying template parsing with AttributePattern
      • Extending binding language
      • Using the template compiler
      • Attribute mapping
  • Getting to know Aurelia
    • Routing
      • @aurelia/router
        • Getting Started
        • Creating Routes
        • Routing Lifecycle
        • Viewports
        • Navigating
        • Route hooks
        • Router animation
        • Route Events
        • Router Tutorial
        • Router Recipes
      • @aurelia/router-lite
        • Getting started
        • Router configuration
        • Configuring routes
        • Viewports
        • Navigating
        • Lifecycle hooks
        • Router hooks
        • Router events
        • Navigation model
        • Transition plan
    • App configuration and startup
    • Enhance
    • Template controllers
    • Understanding synchronous binding
    • Dynamic composition
    • Portalling elements
    • Observation
      • Observing property changes with @observable
      • Effect observation
      • HTML observation
      • Using observerLocator
    • Watching data
    • Dependency injection (DI)
    • App Tasks
    • Task Queue
    • Event Aggregator
  • Developer Guides
    • Animation
    • Testing
      • Overview
      • Testing attributes
      • Testing components
      • Testing value converters
      • Working with the fluent API
      • Stubs, mocks & spies
    • Logging
    • Building plugins
    • Web Components
    • UI virtualization
    • Errors
      • 0001 to 0023
      • 0088 to 0723
      • 0901 to 0908
    • Bundlers
    • Recipes
      • Apollo GraphQL integration
      • Auth0 integration
      • Containerizing Aurelia apps with Docker
      • Cordova/Phonegap integration
      • CSS-in-JS with Emotion
      • DOM style injection
      • Firebase integration
      • Markdown integration
      • Multi root
      • Progress Web Apps (PWA's)
      • Securing an app
      • SignalR integration
      • Strongly-typed templates
      • TailwindCSS integration
      • WebSockets Integration
      • Web Workers Integration
    • Playground
      • Binding & Templating
      • Custom Attributes
        • Binding to Element Size
      • Integration
        • Microsoft FAST
        • Ionic
    • Migrating to Aurelia 2
      • For plugin authors
      • Side-by-side comparison
    • Cheat Sheet
  • Aurelia Packages
    • Validation
      • Validation Tutorial
      • Plugin Configuration
      • Defining & Customizing Rules
      • Architecture
      • Tagging Rules
      • Model Based Validation
      • Validation Controller
      • Validate Binding Behavior
      • Displaying Errors
      • I18n Internationalization
      • Migration Guide & Breaking Changes
    • i18n Internationalization
    • Fetch Client
      • Overview
      • Setup and Configuration
      • Response types
      • Working with forms
      • Intercepting responses & requests
      • Advanced
    • Event Aggregator
    • State
    • Store
      • Configuration and Setup
      • Middleware
    • Dialog
  • Tutorials
    • Building a ChatGPT inspired app
    • Building a realtime cryptocurrency price tracker
    • Building a todo application
    • Building a weather application
    • Building a widget-based dashboard
    • React inside Aurelia
    • Svelte inside Aurelia
    • Synthetic view
    • Vue inside Aurelia
  • Community Contribution
    • Joining the community
    • Code of conduct
    • Contributor guide
    • Building and testing aurelia
    • Writing documentation
    • Translating documentation
Powered by GitBook
On this page
  • Route configuration basics
  • path and parameters
  • Required parameters
  • Optional parameters
  • Wildcard parameters
  • Constrained parameters
  • Setting the title
  • Redirect to another path
  • Fallback: redirecting the unknown path
  • Case sensitive routes
  • Advanced route configuration options
  • Specifying component
  • Using inline import()
  • Using the name
  • Using a function returning the class
  • Using custom element definition
  • Using custom element instance
  • Using conventional HTML-only custom element
  • Using a navigation strategy
  • Using classes as routes
  • Distributed routing configurations
  • Retrieving the current route and query parameters

Was this helpful?

Export as PDF
  1. Getting to know Aurelia
  2. Routing
  3. @aurelia/router-lite

Configuring routes

Learn about configuring routes in Router-Lite.

PreviousRouter configurationNextViewports

Last updated 1 month ago

Was this helpful?

The router takes your routing instructions and matches the URL to one of the configured Routes to determine which components to render. To register routes you can either use the @route decorator or you can use the static routes property to register one or more routes in your application. This section describes the route configuration options in details.

Route configuration basics

The routing configuration syntax for router-lite is similar to that of other routers you might have worked with before. If you have worked with Express.js routing, then the syntax will be very familiar to you.

A route is an object containing a few required properties that tell the router what component to render, what URL it should match on and other route-specific configuration options.

The most usual case of defining a route configuration is by specifying the path and the component properties. The idea is to use the path property to define a pattern, which when seen in the URL path, the view model defined using the component property is activated by the router-lite. Simply put, a routing configuration is a mapping between one or more path patterns to components. Below is the simple example (from the section) of this.

import { route } from '@aurelia/router-lite';
import { Home } from './home';
import { About } from './about';

@route({
  title: 'Aurelia',
  routes: [
    {
      path: ['', 'home'],
      component: Home,
    },
    {
      path: 'about',
      component: About,
    },
  ],
})
export class MyApp {}

For the example above, when the router-lite sees either the path / or /home, it loads the Home component and if it sees the /about path it loads the About component.

Note that the example above uses the @route decorator. In case you cannot use the decorator, you can use the static properties instead. The example shown above can be rewritten as follows.

import { Routeable } from '@aurelia/router-lite';
import { Home } from './home';
import { About } from './about';

export class MyApp {
  // corresponds to the `title` property in the options object used in the @route decorator.
  static title: string = 'Aurelia';

  // corresponds to the `routes` property in the options object used in the @route decorator.
  static routes: Routeable[] = [
    {
      path: ['', 'home'],
      component: Home,
    },
    {
      path: 'about',
      component: About,
    },
  ];
}

As the re-written example shows, you can convert the properties in the options object used for the @route decorator into static properties in the view model class.

Apart from the static API including the @route decorator, there is also an instance-level hook named getRouteConfig that you can use to configure your routes. This is shown in the example below.

import { IRouteConfig, RouteNode } from '@aurelia/router-lite';
import { Home } from './home';
import { About } from './about';

export class MyApp {
  public getRouteConfig(_parentConfig: IRouteConfig | null, _routeNode: RouteNode | null): IRouteConfig {
    return {
      routes: [
        {
          path: ['', 'home'],
          component: Home,
          title: 'Home',
        },
        {
          path: 'about',
          component: About,
          title: 'About',
        },
      ],
    };
  }
}

See this in action below.

Note that the hook is also supplied with a parent route configuration, and the new route node. These values can be nullable; for example, for root node there is no parent route configuration.

The getRouteConfig can also be async. This is shown in the example below.

import { IRouteConfig, RouteNode } from '@aurelia/router-lite';

export class MyApp {
  public getRouteConfig(_parentConfig: IRouteConfig | null, _routeNode: RouteNode | null): IRouteConfig {
    return {
      routes: [
        {
          path: ['', 'home'],
          component: await import('./home').then((x) => x.Home),
          title: 'Home',
        },
        {
          path: 'about',
          component: await import('./about').then((x) => x.About),
          title: 'About',
        },
      ],
    };
  }
}

See this in action below.

path and parameters

The path defines one or more patterns, which are used by the router-lite to evaluate whether or not an URL matches a route or not. A path can be either a static string (empty string is also allowed, and is considered as the default route) without any additional dynamic parts in it, or it can contain parameters. The paths defined on every routing hierarchy (note that routing configurations can be hierarchical) must be unique.

Required parameters

Required parameters are prefixed with a colon. The following example shows how to use a required parameter in the path.

import { route } from '@aurelia/router-lite';
import { Product } from './product';

@route({
  routes: [
    {
      path: 'products/:id',
      component: Product,
    },
  ],
})
export class MyApp {}
import { IRouteViewModel, Params } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';
import template from './product.html';

@customElement({ name: 'pro-duct', template })
export class Product implements IRouteViewModel {
  public canLoad(params: Params): boolean {
    console.log(params.id);
    return true;
  }
}

Note that the value of the id parameter as defined in the route configuration (:id) is available via the params.id. Check out the live example to see this in action.

Optional parameters

Optional parameters start with a colon and end with a question mark. The following example shows how to use an optional parameter in the path.

import { route } from '@aurelia/router-lite';
import { Product } from './product';

@route({
  routes: [
    {
      path: 'product/:id?',
      component: Product,
    },
  ],
})
export class MyApp {}

In the example, shown above, the Product component is loaded when the router-lite sees paths like /product or /product/some-id, that is irrespective of a value for the id parameter. You can see the live example below.

Note that there is an additional link added to the products.html to fetch a random product.

<li>
  <a href="../product">Random product</a>
</li>

As the id parameter is optional, even without a value for the id parameter, clicking the link loads the Product component. Depending on whether or not there is a value present for the id parameter, the Product component generates a random id and loads that.

public canLoad(params: Params): boolean {
  let id = Number(params.id);
  if (Number.isNaN(id)) {
    id = Math.ceil(Math.random() * 30);
  }

  this.promise = this.productService.get(id);
  return true;
}

Wildcard parameters

The wildcard parameters, start with an asterisk instead of a colon, act as a catch-all, capturing everything provided after it. The following example shows how to use a wildcard parameter in the path.

import { route } from '@aurelia/router-lite';
import { Product } from './product';

@route({
  routes: [
    {
      id: 'foo',
      path: ['product/:id', 'product/:id/*rest'],
      component: Product,
    },
  ],
})
export class MyApp {}

In the example, shown above, the Product component is loaded when the router-lite sees paths like /product/some-id or /product/some-id/foo/bar. You can see the live example below.

The example utilizes a wildcard parameter named rest, and when the value of rest is 'image', an image for the product is shown. To this end, the canLoad hook of the Product view-model reads the rest parameter.

public canLoad(params: Params): boolean {
  const id = Number(params.id);
  this.promise = this.productService.get(id);
  this.showImage = params.rest == 'image';
  return true;
}

Constrained parameters

Any required and optional parameters can be constrained by using a regular expression.

The following example shows how to use a wildcard parameter in the path.

import { route } from '@aurelia/router-lite';
import { Product } from './product';

@route({
  routes: [
    {
      id: 'foo',
      path: ['product/:id{{^\\d+$}}'],
      component: Product,
    },
  ],
})
export class MyApp {}

Note that the syntax to define a parameter constraint is as follows.

:PARAM_NAME{{REGEX_CONSTRAINT}}

The example above shows that the Product component is loaded when the router-lite sees paths like /product/123, but not /product/abc.

You can see the live example below.

Note that ^ and $ implies that the value of complete path segment must match the regular expression. You are however free to choose any regular expression that fits your needs. For example, 'product/:id{{^\\d+}}' is also a valid constraint and will match paths like /product/123, /product/123abc etc.

Setting the title

You can configure the title for the routes while you are configuring the routes. The title can be configured in the root level, as well as in the individual route level. This can be seen in the following example using the @route decorator.

import { route, IRouteViewModel } from '@aurelia/router-lite';
@route({
    title: 'Aurelia', // <-- this is the base title
    routes: [
      {
        path: ['', 'home'],
        component: import('./components/home-page'),
        title: 'Home',
      }
    ]
})
export class MyApp implements IRouteViewModel {}

If you prefer using the static routes property, the title can be set using a static title property in the class. The following example has exactly the same effect as of the previous example.

import { IRouteViewModel, Routeable } from "aurelia";
export class MyApp implements IRouteViewModel {
  static title: string = 'Aurelia'; // <-- this is the base title
  static routes: Routeable[] = [
    {
      path: ['', 'home'],
      component: import('./components/home-page'),
      title: 'Home',
    }
  ];
}

Note that, instead of a string, a function can also be used for title to lazily set the title.

Redirect to another path

By specifying the redirectTo property on our route, we can create route aliases. These allow us to redirect to other routes. In the following example, we redirect our default route to the home page and the about-us to about page.

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

You can see this action below.

Note that redirection also works when there are multiple paths/aliases defined for the same component.

@route({
  routes: [
    { path: 'foo', redirectTo: 'home' },
    { path: 'bar', redirectTo: 'about' },
    { path: 'fizz', redirectTo: 'about-us' },
    {
      path: ['', 'home'],
      component: Home,
      title: 'Home',
    },
    {
      path: ['about', 'about-us'],
      component: About,
    },
  ],
})
export class MyApp {}

You can see this action below.

You can use route parameters for redirectTo. The following example shows that the parameters from the about-us path is rearranged to the about path.

import { route } from '@aurelia/router-lite';
import { About } from './about';

@route({
  routes: [
    { path: 'about-us/:foo/:bar', redirectTo: 'about/:bar/:foo' },
    {
      path: 'about/:p1?/:p2?',
      component: About,
      title: 'About',
    },
  ],
})
export class MyApp {}

You can see this action below.

Fallback: redirecting the unknown path

We can instruct the router-lite to redirect the users to a different configured path, whenever it sees any unknown/un-configured paths. To this end, we can use the fallback configuration option. Following example shows how to use this configuration option.

As the example shows, the fallback is configured as follows.

import { route } from '@aurelia/router-lite';
import template from './my-app.html';
import { Home } from './home';
import { About } from './about';
import { NotFound } from './not-found';

@route({
  routes: [
    {
      path: ['', 'home'],
      component: Home,
      title: 'Home',
    },
    {
      path: 'about',
      component: About,
      title: 'About',
    },
    {
      path: 'notfound',
      component: NotFound,
      title: 'Not found',
    },
  ],
  fallback: 'notfound', // <-- fallback configuration
})
export class MyApp {}

There is a custom element, named NotFound, which is meant to be loaded when any unknown/un-configured route is encountered. As you can see in the above example, clicking the "Foo" link that is with un-configured href, leads to the NotFound view.

It is recommended that you configure a fallback at the root to handle the navigation to un-configured routes gracefully.

The name of the custom element, meant to be displayed for any un-configured route can also be used to define fallback. The following example demonstrates this behavior, where not-found, the name of custom element NotFound, is used to refer the route.

An important point to note here is that when you are using the custom element name as fallback, you need to ensure that the custom element is registered to the DI container. Note that in the example above, the NotFound component is registered to the DI container in main.ts.

A function can be used for fallback. The function takes the following signature.

fallback(viewportInstruction: ViewportInstruction, routeNode: RouteNode, context: IRouteContext): string;

An example can look like below, where the example redirects the user to NF1 component if an attempt to load a path /foo is made. Every other attempt to load an unknown path is results loading the NF2 component.

import { customElement } from '@aurelia/runtime-html';
import {
  IRouteContext,
  ITypedNavigationInstruction_string,
  route,
  RouteNode,
  ViewportInstruction,
} from '@aurelia/router-lite';

@customElement({ name: 'ce-a', template: 'a' })
class A {}

@customElement({ name: 'n-f-1', template: 'nf1' })
class NF1 {}

@customElement({ name: 'n-f-2', template: 'nf2' })
class NF2 {}

@route({
  routes: [
    { id: 'r1', path: ['', 'a'], component: A },
    { id: 'r2', path: ['nf1'], component: NF1 },
    { id: 'r3', path: ['nf2'], component: NF2 },
  ],
  fallback(vi: ViewportInstruction, _rn: RouteNode, _ctx: IRouteContext): string {
    return (vi.component as ITypedNavigationInstruction_string).value === 'foo' ? 'r2' : 'r3';
  },
})
@customElement({
  name: 'my-app',
  template: `
  <nav>
  <a href="a">A</a>
  <a href="foo">Foo</a>
  <a href="bar">Bar</a>
</nav>

<au-viewport></au-viewport>`
})
export class MyApp {}

You can also see this in action below.

You can also use non-string fallbacks. For example, you can use a class as the value for fallback; such as fallback: NotFound. Or, if you are using a function, you choose to return a class instead of returning a string. These combinations are also supported by router-lite.

Case sensitive routes

Routes can be marked as case-sensitive in the configuration, allowing the navigation to the component only when the case matches exactly the configured path. See the example below where the navigation to the "about" page is only successful when the casing matches.

import { route } from '@aurelia/router-lite';
import { About } from './about';

@route({
  routes: [
    {
      path: 'AbOuT',
      component: About,
      caseSensitive: true,
    },
  ],
})
export class MyApp {}

Thus, only an attempt to the /AbOuT path loads the About component; any attempt with a different casing is navigated to the fallback. See this in action below.

Advanced route configuration options

There are few other routing configuration which aren't discussed above. Our assumption is that these options are more involved and might not be used that often. Moreover, to understand the utility of these options fully, knowledge of other parts of the route would be beneficial. Therefore, this section only briefly introduces these options providing links to the sections with detailed examples.

Specifying component

Before finishing the section on the route configuration, we need to discuss one last topic for completeness, and that is how many different ways you can configure the component. Throughout various examples so far we have seen that components are configured by importing and using those in the routing configuration. However, there are many other ways in which the components can be configured. This section discusses those.

Using inline import()

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';

  @route({
    routes: [
      {
        path: ['', 'home'],
-       component: Home,
+       component: import('./home'),
        title: 'Home',
      },
      {
        path: 'about',
-       component: About,
+       component: import('./about'),
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

You can see this in action below.

If you are using TypeScript, ensure that the module property set to esnext in your tsconfig.json to support inline import statements.

Using the name

Components can be configured using only the custom-element name of the component.

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';

  @route({
    routes: [
      {
        path: ['', 'home'],
-       component: Home,
+       component: 'ho-me', // <-- assuming that Home component has the name 'ho-me'
        title: 'Home',
      },
      {
        path: 'about',
-       component: About,
+       component: 'ab-out', // <-- assuming that About component has the name 'ab-out'
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

However, when configuring the route this way, you need to register the components to the DI.

// main.ts
import { RouterConfiguration } from '@aurelia/router-lite';
import { Aurelia, StandardConfiguration } from '@aurelia/runtime-html';
import { About } from './about';
import { Home } from './home';
import { MyApp as component } from './my-app';

(async function () {
  const host = document.querySelector<HTMLElement>('app');
  const au = new Aurelia();
  au.register(
    StandardConfiguration,
    RouterConfiguration,

    // component registrations
    Home,
    About,
  );
  au.app({ host, component });
  await au.start();
})().catch(console.error);

You can see this configuration in action below.

Using a function returning the class

Components can be configured using a function that returns a class.

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';

  @route({
    routes: [
      {
        path: ['', 'home'],
-       component: Home,
+       component: () => {
+         @customElement({ name: 'ho-me', template: '<h1>${message}</h1>' })
+         class Home {
+           private readonly message: string = 'Welcome to Aurelia2 router-lite!';
+         }
+         return Home;
+       },
        title: 'Home',
      },
      {
        path: 'about',
-       component: About,
+       component: () => {
+         @customElement({ name: 'ab-out', template: '<h1>${message}</h1>' })
+         class About {
+           private readonly message = 'Aurelia2 router-lite is simple';
+         }
+         return About;
+       },
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

You can see this configuration in action below.

Using custom element definition

Components can be configured using custom element definition.

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';

+ class Home {
+   private readonly message: string = 'Welcome to Aurelia2 router-lite!';
+ }
+ const homeDefn = CustomElementDefinition.create(
+   { name: 'ho-me', template: '<h1>${message}</h1>' },
+   Home
+ );
+ CustomElement.define(homeDefn, Home);
+
+ class About {
+   private readonly message = 'Aurelia2 router-lite is simple';
+ }
+ const aboutDefn = CustomElementDefinition.create(
+   { name: 'ab-out', template: '<h1>${message}</h1>' },
+   About
+ );
+ CustomElement.define(aboutDefn, About);

  @route({
    routes: [
      {
        path: ['', 'home'],
-       component: Home,
+       component: homeDefn,
        title: 'Home',
      },
      {
        path: 'about',
-       component: About,
+       component: aboutDefn,
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

You can see this configuration in action below.

Using custom element instance

Components can be configured using custom element instance.

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';

+ @customElement({ name: 'ho-me', template: '<h1>${message}</h1>' })
+ class Home {
+   private readonly message: string = 'Welcome to Aurelia2 router-lite!';
+ }
+
+ @customElement({ name: 'ab-out', template: '<h1>${message}</h1>' })
+ class About {
+   private readonly message = 'Aurelia2 router-lite is simple';
+ }

  @route({
    routes: [
      {
        path: ['', 'home'],
-       component: Home,
+       component: new Home(),
        title: 'Home',
      },
      {
        path: 'about',
-       component: About,
+       component: new About(),
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

You can see this configuration in action below.

Using conventional HTML-only custom element

When using HTML-only custom elements, facilitated via the convention, the custom element can be directly used while configuring routes.

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';
+ import * as About from './about.html';
+ import * as Home from './home.html';

  @route({
    routes: [
      {
        path: ['', 'home'],
        component: Home,
        title: 'Home',
      },
      {
        path: 'about',
        component: About,
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

Using import() function directly is also supported.

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';

  @route({
    routes: [
      {
        path: ['', 'home'],
-       component: Home,
+       component: import('./home.html'),
        title: 'Home',
      },
      {
        path: 'about',
-       component: About,
+       component: import('./about.html'),
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

When importing the HTML-only custom elements in the HTML file using <require from=""> syntax, use the custom element name in the route configuration.

  <require from="./about.html"></require>
  <require from="./home.html"></require>

  <au-viewport></au-viewport>
  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
- import { About } from './about';
- import { Home } from './home';

  @route({
    routes: [
      {
        path: ['', 'home'],
-       component: Home,
+       component: 'home',
        title: 'Home',
      },
      {
        path: 'about',
-       component: About,
+       component: 'about',
        title: 'About',
      },
    ],
  })
@customElement({ name: 'my-app', template })
export class MyApp {}

Using a navigation strategy

Components can be configured using a navigation strategy. A navigation strategy is an instance of NavigationStrategy class that takes a factory method to return a routable component. The following example shows how to use a navigation strategy.

  import { customElement } from '@aurelia/runtime-html';
  import { route } from '@aurelia/router-lite';
  import template from './my-app.html';
  import { About } from './about';
  import { Home } from './home';

+ let dataLoaded = false;

  @route({
    routes: [
      {
        path: ['', 'home'],
        component: Home,
        title: 'Home',
      },
      {
        path: 'about',
        component: About,
        title: 'About',
      },
+     {
+      path: 'foo',
+      component: new NavigationStrategy(() => {
+        if (dataLoaded) return Home;
+
+        dataLoaded = true;
+        return About;
+      })
+     }
    ],
  })
  @customElement({ name: 'my-app', template })
  export class MyApp {}

The factory method takes has the following signature.

type NavigationStrategyComponent = string | RouteType | Promise<IModule> | CustomElementDefinition;
function getComponent(viewportInstruction: IViewportInstruction, ctx: IRouteContext, node: RouteNode, route: RecognizedRoute<unknown>): string | RouteType | Promise<IModule> | CustomElementDefinition;

The parameters can be used further to determine which component to load.

Using classes as routes

Using router-lite it is also possible to use the routed view model classes directly as routes configuration. While doing so, if no paths have been explicitly configured for the components, the custom element name and aliases can be used as routing instructions. The following example demonstrates that the C1 and C2 classes are used directly as the child routes for the Root.

@customElement({ name: 'c-1', template: 'c1', aliases: ['c-a', 'c-one'] })
class C1 { }

@customElement({ name: 'c-2', template: 'c2', aliases: ['c-b', 'c-two'] })
class C2 { }

@route({
  routes: [C1, C2]
})
@customElement({ name: 'ro-ot', template: '<au-viewport></au-viewport>' })
class Root { }

The example above implies that router.load('c-1'), or router.load('c-a') and router.load('c-2'), router.load('c-two') will load the C1 and C2 respectively.

Distributed routing configurations

The examples discussed so far demonstrate the classic use-cases of route configurations where the parents define the child routes. Another aspect of these examples are that all the route configurations are centralized on the parent component. This section provides some examples where that configuration is distributed across different components.

We start by noting that every component can define its own path. This is shown in the following example.

import { customElement } from '@aurelia/runtime-html';
import { route } from '@aurelia/router-lite';
import { Home } from './home';
import { About } from './about';

@route({
  routes: [Home, About],
})
@customElement({
  name: 'my-app',
  template: `
<nav>
  <a href="home">Home</a>
  <a href="about">About</a>
</nav>

<au-viewport></au-viewport>
`
})
export class MyApp {}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route(['', 'home'])
@customElement({ name: 'ho-me', template: '<h1>${message}</h1>' })
export class Home {
  private readonly message: string = 'Welcome to Aurelia2 router-lite!';
}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route('about')
@customElement({ name: 'ab-out', template: '<h1>${message}</h1>' })
export class About {
  private readonly message = 'Aurelia2 router-lite is simple';
}

The example shows that both Home and About uses the @route decorator to define their own paths. This reduces the child-route configuration for MyApp to @route({ routes: [Home, About] }). The example can be seen in action below.

Note that other properties of route configuration can also be used in this way.

import { customElement } from '@aurelia/runtime-html';
import { route } from '@aurelia/router-lite';
import { Home } from './home';
import { About } from './about';

@route({
  routes: [Home, About],
})
@customElement({
  name: 'my-app',
  template: `
<nav>
  <a href="home">Home</a>
  <a href="about">About</a>
</nav>

<au-viewport></au-viewport>
`
})
export class MyApp {}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route({ path: ['', 'home'], title: 'Home' })
@customElement({ name: 'ho-me', template: '<h1>${message}</h1>' })
export class Home {
  private readonly message: string = 'Welcome to Aurelia2 router-lite!';
}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route({ path: 'about', title: 'About' })
@customElement({ name: 'ab-out', template: '<h1>${message}</h1>' })
export class About {
  private readonly message = 'Aurelia2 router-lite is simple';
}

The previous example demonstrates that the Home and About components define the title for themselves. The example can be seen in action below.

While adapting to distributes routing configuration, the parent can still override the configuration for its children. This makes sense, because even if a component defines its own path, title etc. the parent may choose to reach (route) the component via a different path or display a different title when the component is loaded. This is shown below, where the MyApp overrides the routing configurations defined by About.

import { customElement } from '@aurelia/runtime-html';
import { route } from '@aurelia/router-lite';
import { Home } from './home';
import { About } from './about';

@route({
  routes: [
    Home,
    { path: 'about-us', component: About, title: 'About us' }
  ],
})
@customElement({
  name: 'my-app',
  template: `
<nav>
  <a href="home">Home</a>
  <a href="about-us">About</a>
</nav>

<au-viewport></au-viewport>
`
})
export class MyApp {}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route({ path: ['', 'home'], title: 'Home' })
@customElement({ name: 'ho-me', template: '<h1>${message}</h1>' })
export class Home {
  private readonly message: string = 'Welcome to Aurelia2 router-lite!';
}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route({ path: 'about', title: 'About' })
@customElement({ name: 'ab-out', template: '<h1>${message}</h1>' })
export class About {
  private readonly message = 'Aurelia2 router-lite is simple';
}

This can be seen in action below.

You can also use the @route decorator and the getRouteConfig together.

import { customElement } from '@aurelia/runtime-html';
import {
  IRouteConfig,
  IRouteViewModel,
  route,
  RouteNode,
} from '@aurelia/router-lite';
import template from './my-app.html';
import { Home } from './home';
import { About } from './about';

@route({ title: 'Aurelia2' })
@customElement({
  name: 'my-app',
  template: `
<nav>
  <a href="home">Home</a>
  <a href="about">About</a>
</nav>

<au-viewport></au-viewport>
`
})
export class MyApp implements IRouteViewModel {
  public getRouteConfig?(
    parentConfig: IRouteConfig | null,
    routeNode: RouteNode | null
  ): IRouteConfig {
    return {
      routes: [Home, About],
    };
  }
}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route({ path: ['', 'home'], title: 'Home' })
@customElement({ name: 'ho-me', template: '<h1>${message}</h1>' })
export class Home {
  private readonly message: string = 'Welcome to Aurelia2 router-lite!';
}
import { route } from '@aurelia/router-lite';
import { customElement } from '@aurelia/runtime-html';

@route({ path: 'about', title: 'About' })
@customElement({ name: 'ab-out', template: '<h1>${message}</h1>' })
export class About {
  private readonly message = 'Aurelia2 router-lite is simple';
}

This can be seen in action below.

The advantage of this kind of distributed configuration is that the routing configuration of every component is defined by every component itself, thereby encouraging encapsulation, leaving the routing configuration at the parent level to merely listing out its child components. On the other hand, highly distributed route configuration may prevent easy overview of the configured routes. That's the trade-off. Feel free to mix and match as per your need and aesthetics.

Retrieving the current route and query parameters

Apart from configuring routes, you may also want to read which route is currently active and obtain any query parameters. If you only defined or named a few of them as route parameters, others might appear as query parameters. Here is a short example:

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

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

  attached() {
    console.log('Current path:', this.currentRoute.path);
    console.log('Query params:', this.currentRoute.url.split('?')[1] ?? 'no query');
    // Or, to see them as a structured object:
    console.log('Parsed query:', Object.fromEntries(this.currentRoute.parameterInformation[0].params ?? {}));
  }
}

Note that you can map multiple paths to a single component. Although these paths can be thought of as aliases, multiple paths, in combination with gets interesting. Another way of creating aliases is to use the configuration option.

When a given URL matches one such route, the parameter value is made available in the canLoad, and load .

With this configuration in place, the default-built title will be Home | Aurelia when user is navigated to / or /home route. That is, the titles of the child routes precedes the base title. You can customize this default behavior by using a when customizing the router configuration.

Another way of defining the fallback is to use the . The following example demonstrates this behavior, where the NotFound view can be reached via multiple aliases, and instead of choosing one of these aliases the route-id is used to refer the route.

A fallback defined on parent is inherited by the children (to know more about hierarchical routing configuration, refer the ). However, every child can override the fallback as needed. The following example demonstrate this. The root has two and two children components can be loaded into each of those by clicking the link. Every child defines their own child routing configuration. The root defines a fallback and one of the children overrides the fallback by defining one of its' own. With this configuration in place, when navigation to a un-configured route ('Foo') is attempted for each children, one loads the overridden version whereas the other loads the fallback inherited from the parent (in this case the root).

id — The unique ID for this route. The router-lite implicitly generates a id for a given route, if an explicit value for this property is missing. Although this is not really an advanced property, due to the fact that a route can be uniquely identified with id, it can be used in many interesting ways. For example, this can be used to generate the hrefs in the view when using the or using the . Using this property is also very convenient when there are multiple aliases for a single route, and we need a unique way to refer to this route.

transitionPlan — How to behave when the currently active component is scheduled to be loaded again in the same viewport. For more details, please refer the .

viewport — The name of the viewport this component should be loaded into. This demands a full fledged documentation of its own. Refer to the for more details.

data — Any custom data that should be accessible to matched components or hooks. The value of this configuration property must be an object and the object can take any shape (that is there is no pre-defined interface/class for this object). A typical use-case for the data property is to define the permissions, required by the users, when they attempt to navigate to this route. Refer of this.

nav - Set this flag to false (default value is true), to instruct the router not to add the route to the . This is typically useful to from the public navigation menu.

Components can be configured using the or dynamic import. Instead of statically importing the components, those can be imported using import()-syntax, as the example shows below.

To know more about the router API refer .

You can inject ICurrentRoute in any routed view-model. For an elaborate approach, see additional references in the and .

getting started
routing hooks
documentation
import()
path parameters
redirectTo
route-id
navigation model
navigation docs
Logorouter-lite - getRouteConfig hook - StackBlitzStackBlitz
Logorouter-lite - async getRouteConfig hook - StackBlitzStackBlitz
Logorouter-lite - required param - StackBlitzStackBlitz
exclude routes
navigation model
Logorouter-lite - optional param - StackBlitzStackBlitz
Logorouter-lite - wildcard param - StackBlitzStackBlitz
Logorouter-lite - redirect - StackBlitzStackBlitz
Logorouter-lite - redirect - multiple paths - StackBlitzStackBlitz
Logorouter-lite - redirect - parameterized - StackBlitzStackBlitz
Logorouter-lite - fallback - StackBlitzStackBlitz
Logorouter-lite - fallback - using routeid - StackBlitzStackBlitz
Logorouter-lite - fallback - using ce name - StackBlitzStackBlitz
Logorouter-lite - fallback - hierarchical - StackBlitzStackBlitz
Logorouter-lite - fallback - using function - StackBlitzStackBlitz
Logorouter-lite - case-sensitive - StackBlitzStackBlitz
Logorouter-lite - component - inline import - StackBlitzStackBlitz
Logorouter-lite - component - ce-name - StackBlitzStackBlitz
Logorouter-lite - component - function - StackBlitzStackBlitz
Logorouter-lite - component - ce-defn - StackBlitzStackBlitz
Logorouter-lite - component - ce-instance - StackBlitzStackBlitz
Logorouter-lite - path on vm - StackBlitzStackBlitz
Logorouter-lite - route config on vm - StackBlitzStackBlitz
Logorouter-lite - parent overrides comp route config - StackBlitzStackBlitz
Logorouter-lite - hybrid config - StackBlitzStackBlitz
custom buildTitle function
documentation
sibling viewports
viewport documentation
an example
load custom attribute
Router#load API
this section