> Markdown version of [Routing & Navigation](https://vaadin.com/docs/v25/hilla/lit/guides/routing). Section index: [llms.txt](https://vaadin.com/docs/v25/hilla/llms.txt)

# Routing & Navigation

## <a id="routes"></a>Defining Routes

Routes for views or sub-views are defined in the `frontend/routes.ts` file.

`frontend/routes.ts`

```typescript
import { Route } from '@vaadin/router';
import './views/helloworld/hello-world-view'; // (1)
import './views/main-layout';


export const views: Route[] = [
  {
    path: '',
    component: 'hello-world-view', // (2)
    title: '',
  },
  {
    path: 'hello',
    component: 'hello-world-view',
    title: 'Hello World',
  },
  {
    path: 'about',
    component: 'about-view',
    title: 'About',
    action: async () => await import('./views/about/about-view'), // (3)
  },
];

export const routes: Route[] = [
  {
    path: '',
    component: 'main-layout',
    children: [...views], // (4)
  },
];
```

1. Import views in `routes.ts` to include them in the main bundle. You should only include those views that are needed on startup to keep the JavaScript bundle size as small as possible.

2. Map view tag names to paths in the route definition object.

3. Use dynamic imports to import other views. This way, the views and their dependencies will only get downloaded if the user navigates to the view.

4. Optional: define a main layout for things like header, footer, and navigation. Include the views as children.

### <a id="initializing-the-router"></a>Initializing the Router

The router is initialized in `frontend/index.ts` using the routes defined in `frontend/routes.ts`.

`frontend/index.ts`

```typescript
import { Router } from '@vaadin/router';
import { routes } from './routes';

export const router = new Router(document.querySelector('#outlet')); // (1)

router.setRoutes(routes); // (2)
```

1. Create a new `Router` instance that outputs its content into the element that has the id `outlet` in `index.html`.

2. Register the routes defined in `routes.ts` with the Router.

## <a id="lifecycle"></a>Navigation Lifecycle

The router executes callbacks on each view to check whether the navigation should continue, be postponed or be redirected.

You can choose to implement any of the following lifecycle interfaces and their corresponding callback methods in your view components. **All lifecycle callbacks are asynchronous**.

- `BeforeEnterObserver`

  The `onBeforeEnter(location, commands, router)` callback is executed before the outlet container is updated with the new element.

  At this point, the user can cancel the navigation.

- `AfterEnterObserver`

  The `onAfterEnter(location, commands, router)` callback is executed after the new element has been attached to the outlet.

  The difference between this method and `onBeforeEnter()` is that, when this method is executed, there is no way to cancel the navigation.

- `BeforeLeaveObserver`

  The `onBeforeLeave(location, commands, router)` callback is executed before the previous element is detached.

  Navigation can be cancelled at this point.

- `AfterLeaveObserver`

  The `onAfterLeave(location, commands, router)` callback is executed before the element is removed from the DOM.

  When this method is executed, there is no way to cancel the navigation.

  During the execution of the `onBeforeEnter()` and `onBeforeLeave()` callbacks, the user can postpone the navigation by returning `commands.prevent()`. Uniquely in `onBeforeEnter()`, navigation can be redirected by returning `commands.redirect(path)`.

The following example show how to cancel navigation in a view component:

`my-view.ts`

```typescript
import { LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import {
  BeforeEnterObserver,
  PreventAndRedirectCommands,
  Router,
  RouterLocation
} from '@vaadin/router';

@customElement('my-view')
class MyView extends LitElement implements BeforeEnterObserver {
  onBeforeEnter(
      location: RouterLocation,
      commands: PreventAndRedirectCommands,
      router: Router) {
    if (location.pathname === '/cancel') {
      return commands.prevent();
    }
  }
}
```

## <a id="nested-routes"></a>Nested Routes & Views

In many typical applications, you have a main view that displays a menu allowing the user to choose a child view to display. When the user selects an item from the menu, a specific child view is shown in a content area inside the main view.

You can define such a main view on either the server side or the client side. However, if you intend to display any client-side child views, the main view must be a client-side view.

A main view typically:

- imports Lumo or Aura theme global styles,

- establishes the nested view structure with `<vaadin-app-layout>`,

- creates a navigation menu bar,

- generates menu links using the `router` instance,

- has a binding for the selected tab.

You can have multiple such main views.

### <a id="route-configuration"></a>Route Configuration

In a nested view configuration, you have a route to the main view, and child routes to the sub-views. The route to the main view is usually the root route. You can configure the child views either with explicit full paths, such as `/main-view/users`, or hierarchically with child routes, as follows.

The following configuration in `routes.ts` sets up a main view with two child views:

```typescript
const routes = [
{
	path: '',
	component: 'main-view',
	children: [
		{
			path: '',
			component: 'hello-world-view',
		},
		{
			path: 'about',
			component: 'about-view',
			action: async () => { await import ('./views/about/about-view'); }
		}
	]
},
];
```

### <a id="establish-an-application-layout"></a>Establish an Application Layout

The most prominent feature of the main layout is that it defines the layout for the application. You can use the [App Layout](https://vaadin.com/docs/components/app-layout) component:

main-layout.ts

```typescript
import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { Layout } from './view';
import '@vaadin/app-layout';

@customElement('main-layout')
export class MainLayout extends Layout {
  render() {
    return html`
      <vaadin-app-layout>
        <slot></slot>
      </vaadin-app-layout>
    `;
  }
}
```

> **Note:** Keep the `<slot>` in the main layout template returned from the `render()` method. Hilla `Router` adds views as children in the main layout.

### <a id="create-the-navigation-menu"></a>Create the Navigation Menu

The main layout usually contains a navigation bar with the menu. Here, we create the navigation bar with menu using plain anchor tags:

main-layout.ts

```typescript
import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { Layout } from './view';
import '@vaadin/app-layout';

@customElement('main-layout')
export class MainLayout extends Layout {
  render() {
    return html`
      <vaadin-app-layout id="layout">
        <div slot="drawer">
          <a href="/">Hello world</a>
          <a href="/about">About</a>
        </div>
        <slot></slot>
      </vaadin-app-layout>
    `;
  }
}
```

### <a id="create-the-header"></a>Create the Header

You can The App Layout component supports a header by adding content to the `navbar` slot.

main-layout.ts

```typescript
import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import { Layout } from './view';
import '@vaadin/app-layout';
import '@vaadin/app-layout/vaadin-drawer-toggle.js';

@customElement('main-layout')
export class MainLayout extends Layout {
  render() {
    return html`
      <vaadin-app-layout id="layout">
        <header slot="navbar">
          <vaadin-drawer-toggle aria-label="Menu toggle">
          </vaadin-drawer-toggle><!--(1)-->
          <h1>App Title</h1>
        </header>
        <div slot="drawer">
          <a href="/">Hello world</a>
          <a href="/about">About</a>
        </div>
        <slot></slot>
      </vaadin-app-layout>
    `;
  }
}
```

1. The `<vaadin-drawer-toggle>` element is a button for hiding and showing the navigation drawer.

## <a id="parameters"></a>Route Parameters

Route parameters are useful when the same Web Component needs to be rendered for multiple paths, where part of the path is static, and another part contains a parameter value.

For example, the paths `/user/1` and `/user/42` can both have the same route to render the content. The `/user/` part is static, and `1` and `42` are the parameter values.

Route parameters are defined using an `express.js`-like syntax. The implementation is based on the `path-to-regexp` library, which is commonly used in modern frontend libraries and frameworks.

The following features are supported:

- Named parameters

  `/profile/:user`

- Optional parameters

  `/:size/:color?`

- Zero-or-more segments

  `/kb/:path*`

- One-or-more segments

  `/kb/:path+`

- Custom parameter patterns

  `/image-:size(\d+)px`

- Unnamed parameters

  `/(user[s]?)/:id`

Routes for these features can be defined as follows:

```typescript
const router = new Router(document.getElementById('outlet'));
router.setRoutes([
  {path: '/', component: 'home-view'},
  {path: '/profile/:user', component: 'user-profile'},
  {path: '/image/:size/:color?', component: 'image-view'},
  {path: '/kb/:path*', component: 'knowledge-base'},
  {path: '/image-:size(\\d+)px', component: 'image-view'},
  {path: '/(user[s]?)/:id', component: 'profile-view'},
]);
```

### <a id="accessing-route-parameters"></a>Accessing Route Parameters

Route parameters can be accessed in the `location.params` property of the route component. The `location` property is defined by the router.

Named parameters are accessible by a string key, such as `location.params.id` or `location.params['id']`.

Unnamed parameters are accessible by a numeric index, such as `location.params[0]`.

example-view\.ts

```typescript
import { BeforeEnterObserver, Router, RouterLocation } from '@vaadin/router';
import { View } from '../../views/view';

@customElement('user-view')
export class CreateOrUpdatePetView extends View
  implements BeforeEnterObserver { // (1)

  @state() id?;

  onBeforeEnter(location: RouterLocation) { // (2)
    this.id = parseInt(location.params.id as string);
  }

  render(){
    return html`
      <h1>Viewing user with id ${this.id}</h1>
    `;
  }
}
```

1. Implement `BeforeEnterObserver` in your view. See [Navigation lifecycle](#lifecycle) for a list of different lifecycle callbacks for views.

2. Implement the `onBeforeEnter()` callback to read the parameter value.
