Nested LWR Client-Side Routing Example

You can use the @lwrjs/router package to create a single page application (SPA) with nested client-side routing.

Before creating an SPA with nested routing, check out:

While an LWR app contains more files than shown here, in this recipe the following example module files are most important:

This example describes creating an SPA with two levels of client-side routing. The root component (example/app) owns the parent router, and the example/animal component owns the child router. The sitemap for the app is structured like this:

OwnerView ComponentURL
parent routerexample/home/
parent routerexample/animal/animal
child routerexample/animalHome/animal/
child routerexample/animalCat/animal/cat
child routerexample/animalDog/animal/dog

The child router lives under the /animal parent URL, so all of its absolute URLs are prefixed with /animal. The parent router owns the beginning URL segments (/ and /animal), while the child router owns the ending URL segments (/, /cat, and /dog).

If a user navigates to any of the /animal/* URLs, they find two view components on the page:

  1. example/animal from the parent router
  2. example/animalHome, example/animalCat, or example/animalDog from the child router

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 example below and "Routing Properties" for more information.

The routes defined in lwr.config.json are server-side. They differ from the RouteDefinition array that you set up for the client-side routers.

After you set up your root component, you create a router in it.

Start by importing the createRouter() function from lwr/router. Next, define a RouteDefinition array and pass that array to createRouter().

Finally, the createRouter() function returns a router instance for use by lwr/routerContainer.

Since the preceding child router is attached to the /animal URI, the RouteDefinition MUST have exact: false.

In your root component’s html file, attach the router instance to lwr/routerContainer:

In the previous step you created a router in your root component. That router includes promises in RouteDefinition.handler to route handler modules that are called when a location matches a RouteDefinition. Route handler modules determine the associated "view" that’s displayed when the application navigates to a location. They also allow you to define enhanced routing rules such as branching and pivoting logic based on data and metadata values.

In LWR, modules are always provided via promises. Promises allow the module code to be lazily loaded, improving application performance.

For the root component in this example, the homePageHandler and animalPageHandler modules each have forms similar to:

Similarly to how you created a parent router, you create a child router in example/animal:

And in the associated html file, you attach the router instance to lwr/routerContainer:

You create the child route handlers similarly to how you created parent route handlers.

The first child route handler, animalHomePageHandler, has a form similar to that of the homePageHandler and animalPageHandler modules discussed previously:

The animalNamedPageHandler module, however, is more complicated. Here the module is determining the view based on branching logic:

Simple Routing for an SPA provides an example of handling navigation in your application.

Run the following terminal commands from the root of your project:

Open the site at http://localhost:3000. Read Get Started for details on LWR Yarn commands.