---
schema_version: "1"
id: a34906ab26253b649b9ca30872357398f51a0bf33994577b45b6a6ce421adfee
title: Simple LWR Client-Side Routing Example
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/lwr-node/lwr/guides/lwr-routing-example-simple.md
canonical_url: https://developer.salesforce.com/docs/platform/lwr/guide/lwr-routing-example-simple.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: e74aed7cdcb179c0c7f653cbcef10515d232d820edeaace6e002137a152261cf
products:
  - Lightning Web Runtime
---
# Simple LWR Client-Side Routing Example

You can use the `@lwrjs/router` package to create a single page LWR application (SPA) with client-side routing. Important exports from the package include:

- `lwr/router`
- `lwr/routerContainer`
- `lwr/outlet`
- `lwr/navigation`

For additional information:

- [Client-Side Routing in LWR on Node.js](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-routing-client-overview.md)
- [LWR Client-Side Routing Reference](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/csr-reference.html)

## Example Project Setup

While an LWR app contains more files than shown here, in this recipe the following example module files are most important:

```asciidoc
simple-routing
├── src
│   └── modules
│       └── example
│           ├── app
│           │   ├── app.css
│           │   ├── app.html
│           │   └── app.js
│           ├── homePageHandler
│           │   └── homePageHandler.js
│           ├── namedPageHandler
│           │   └── namedPageHandler.js
│           └── pageLink
│                   └── pageLink.html
│                   └── pageLink.js
├── lwr.config.json
└── package.json
```

## Set the Root Component of Your Application

The first step in creating a router is to set up the root component of your SPA. You specify a `rootComponent` value in a `routes` object in your app’s `lwr.config.json` file. Check out the following example and ["Routing Properties"](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-routing-server.md#routing-properties) for more information.

**note:** The routes defined in `lwr.config.json` are server-side. They differ from the [`RouteDefinition`](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/routedefinition.html) array that you set up in the next step for the client-side router.

This file is lwr.config.json.

```json
{
  "lwc": { "modules": [{ "dir": "$rootDir/src/modules" }] },
  "routes": [
    {
      "id": "app",
      "path": "/",
      "rootComponent": "example/app"
    }
  ]
}
```

## Create a Router

After you set up your root component, create a router for it. This example uses the [`createRouter()`](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-create-router.md) function to generate a router.

<!-- prettier-ignore -->
```javascript
// src/modules/example/app/app.js

import { LightningElement } from "lwc";
import { createRouter } from "lwr/router";

// Route definition array
const routes = [
  {
    id: 'home',
    uri: '/',
    handler: () => import('example/homePageHandler'),
    page: {
      type: 'home',
    },
  },
  {
    id: 'namedPage',
    uri: '/:pageName',
    handler: () => import('example/namedPageHandler'),
    page: {
      type: 'namedPage',
      attributes: {
        pageName: ':pageName',
      },
    },
  },
];

export default class SimpleRoutingApp extends LightningElement {
  router = createRouter({ routes });

  homeReference = { type: 'home' }; // Home page reference for the page-link
}
```

In your root component’s html file, attach the router instance to `lwr/routerContainer`, as in this example:

<!-- prettier-ignore -->
```html
<!-- src/modules/example/app/app.html -->

<template>
    <lwr-router-container router={router}>
        <example-page-link label="Home" page-reference={homeReference}></example-page-link>
        <lwr-outlet>
            <div slot="error">This content failed to display.</div>
        </lwr-outlet>
    </lwr-router-container>
</template>
```

## Create a Route Handler

In the previous step you [created a router](#create-a-router) in your root component file. That router includes a promise in `RouteDefinition.handler` to a route handler module that’s called when a location matches a [`RouteDefinition`](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/routedefinition.html). [Route handler modules](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-route-handler-modules.md) determine the associated "view", which is the component to display when the application navigates to a location.

**note:** In LWR, route handler modules are [dynamically imported](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import), so they are provided via promises. Promises allow the module code to be lazily loaded, improving application performance.

A simple route handler for a home page:

<!-- prettier-ignore -->
```javascript
// src/modules/example/homePageHandler/homePageHandler.js

export default class HomePageHandler {
  callback;

  constructor(callback) {
    this.callback = callback;
  }

  dispose() {
    /* noop */
  }

  update() {
    this.callback({
      viewset: {
        default: () => import('example/home'),
      },
    });
  }
}
```

Route handler modules allow you to define enhanced routing rules such as branching and pivoting logic based on data and metadata values. Here’s another route handler, this time with branching logic:

<!-- prettier-ignore -->
```javascript
// src/modules/example/namedPageHandler/namedPageHandler.js

export default class NamedPageHandler {
  callback;

  constructor(callback) {
    this.callback = callback;
  }

  dispose() {
    /* noop */
  }

  update({ attributes }) {
    let viewGetter;

    // Get the "pageName" from the incoming page reference
    switch (attributes.pageName) {
      case "products":
        viewGetter = () => import('example/products');
        break;
      case "recipes":
        viewGetter = () => import('example/recipes');
        break;
      case "contact":
        viewGetter = () => import('example/contact');
        break;
      default:
        return;
    }

    this.callback({
      viewset: {
        default: viewGetter,
      },
    });
  }
}

```

## Navigate

In a Lightning web component, use the [`NavigationContext` wire](/docs/platform/lwr/references/csr-reference/navigationcontext.html) and [`navigate()` function](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/navigate.html) from `lwr/navigation` to navigate.

For example, in this snippet, the `NavigationContext` wire obtains a `ContextId` value. It assigns the ID to the `navContext` property, where it can be used by the [`navigate()`](/docs/platform/lwr/references/csr-reference/navigate.html) API.

```
import { LightningElement, wire } from 'lwc';
import { NavigationContext, navigate } from 'lwr/navigation';

export default class HomeLink extends LightningElement {
    @wire(NavigationContext)
    navContext;

    navigateHome(event) {
        // Navigate when the button is clicked
        event.preventDefault();
        if (this.navContext) {
            navigate(this.navContext, {
                type: 'home',
                attributes: { format: 'list' },
                state: { 'dark-mode': 'true' },
            });
        }
    }
}
```

```
<template>
  <button onclick="{navigateHome}">Home</button>
</template>
```

You can also use [`generateUrl()`](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/generateurl.html) to generate a URL from a page reference, like this:

```
import { LightningElement, api, track, wire } from 'lwc';
import { NavigationContext, generateUrl, navigate } from 'lwr/navigation';

export default class PageLink extends LightningElement {
  @api label;
  @api pageReference;
  @track path;

  @wire(NavigationContext)
  navContext;

  async connectedCallback() {
    // Add an href to the link anchor
    if (this.pageReference && this.navContext) {
      this.path = generateUrl(this.navContext, this.pageReference) || undefined;
    }
  }

  handleClick(event) {
    // Navigate when the link is clicked
    event.preventDefault();
    if (this.pageReference && this.navContext) {
      navigate(this.navContext, this.pageReference);
    }
  }
}
```

```
<template>
    <a onclick={handleClick} href={path}>{label}</a>
</template>
```

## Handle Errors

Sometimes the router navigation completes, but the component's new view contains an error. This situation is considered a successful navigation, so an [`errornavigate` event](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/lwr-router-container.html#returns) doesn't fire. To handle this situation:

1. Display an error:

   Via an error slot in an [`lwr-outlet`](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/lwr-outlet.html) component:

   ```html
   <!-- app.html -->

   <lwr-outlet>
     <div slot="error">This content failed to display</div>
   </lwr-outlet>
   ```

   Or via a custom component in the error slot:

   ```html
   <!-- app.html -->

   <lwr-outlet>
     <div slot="error">
       <c-my-error></c-my-error>
     </div>
   </lwr-outlet>
   ```

2. Handle the [`viewerror` event](https://developer.salesforce.com/docs/platform/lwr/references/csr-reference/lwr-outlet.html#events) that `lwr-outlet` dispatches:

   ```
<template>
       <lwr-router-container router={router}>
           <lwr-outlet onviewerror={onViewError}></lwr-outlet>
       </lwr-router-container>
   </template>
```

   ```
onViewError(viewErrorEvent: CustomEvent) {
       // handle viewerror
       const error = viewErrorEvent.detail.error;
       const stack = viewErrorEvent.detail.stack;
       console.error(`error rendering view component: '${error.message}' from:\n${stack}`);
   }
```

Read more about [LWR outlets](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-outlets.md).

## Run Your SPA

Run the following terminal commands from the root of your project. Read [Get Started](https://developer.salesforce.com/docs/platform/lwr/guide/lwr-get-started.md#set-the-lwr-server-modes) for details on LWR NPM commands.

```bash
npm install
npm run build
npm run start
```

Open the site at http://localhost:3000.
