LWR Client-Side Routing Reference

LWR provides APIs, interfaces, wires, and Lightning web components to support client-side routing.

The following data types are used in client-side routing:

Because static configuration can be easier to author and to maintain, you can use the Router Module Provider to generate a router based on a static JSON file. LwrRouterConfig and LwrConfigRouteDefinition together specify the configuration of the router module to generate. Alternately, you can use the createRouter() API.

The LwrRouterConfig object contains the same properties that are passed to createRouter():

  • basePath: Optional. String. A path prefix applied to all URIs for the router. If no base path is provided, default = “”
  • caseSensitive: Optional. Boolean. If true, URIs are processed case sensitively. Default = false.
  • routes: An array of LwrConfigRouteDefinition objects.

Similarly, the LwrConfigRouteDefinition object mostly contains the same properties as a RouteDefinition object:

  • id: String. Each LwrConfigRouteDefinition must have a unique identifier.
  • uri: String. A pattern for URI locations that match this LwrConfigRouteDefinition. The following characters are allowed:
    • /: path separator
    • :parameter: captures a variable from a path or query parameter; must be alpha-numeric (a-zA-Z0-9)
    • ?: denotes the beginning of the query string
    • &: query parameter separator
    • *Note: The # and * characters aren't allowed.*
  • page: The form of page references that match this LwrConfigRouteDefinition. page values can be:
    • :parameter bindings: these map a path or query parameter from the URI to an attributes or state property
    • literal bindings: these hard-code the type, an attribute, or state property to a literal value; they must not contain the following reserved characters: ? , * , # , & , + , ; , ( , )
  • patterns: Optional. A regular expression that a parameter must match to be valid. It’s not required, but it is recommended that the regular expression pattern is explicitly wrapped in the start-of-line character (^) and end-of-line-character ($). If you don't use the start-of-line or end-of-line character, the pattern could allow unexpected values. Example: {"patterns": {"parameter": "^id[0-9a-zA-Z]{5}$"}}
  • exact: Optional. Boolean, default = true. If there is a nested router, this value must be false.
  • metadata: Optional. Developer-defined metadata. You can use the metadata property to attach arbitrary additional information to a LwrConfigRouteDefinition. By default, metadata can contain string keys and any type values; you can extend LwrConfigRouteDefinition for required or more strongly typed metadata.
  • One of the following. An LwrConfigRouteDefinition must contain a handler or a component, but not both:
    • handler: A string reference to the RouteHandler class specifier.
    • component: A string reference to the view component specifier. This is a shortcut so the view component can be specified directly, without authoring a route handler.

“Generate a Router with Router Module Provider” provides an example of using LwrRouterConfig and LwrConfigRouteDefinition.

An LWR router receives navigation events as page references. Page references are a JSON-format specification of a location. You pass a PageReference object to the router via the navigate() API.

A PageReference object containing JSON-format locations:

  • type: String.
  • attributes: An array of user-defined key-value pairs or null if there are no attribute values.
  • state: An array of user-defined key-value pairs or null if there are no state values.

Allowed values for attributes and state are determined by the values you set in RouteDefinition.page.

Example PageReference:

The most important part of the RouterConfig is the array of route definitions. The router uses route definitions to verify and process incoming location changes. A location is only valid if it can be matched to a RouteDefinition. If given an invalid location, the application fails to navigate.

Each RouteDefinition has this form:

A RouteDefinition contains the following properties:

  • id: Each RouteDefinition must have a unique identifier.
  • uri: A string pattern for URI locations that match this RouteDefinition. The following characters are allowed:
    • /: path separator
    • :parameter: captures a variable from a path or query parameter; must be alpha-numeric (a-zA-Z0-9)
    • ?: denotes the beginning of the query string
    • &: query parameter separator
    • *Note: The # and * characters are not allowed.*
  • page: The form of page references that match this RouteDefinition. RouteDefinition.page values can be:
    • :parameter bindings: these map a path or query parameter from the URI to an attributes or state property
    • literal bindings: these hard-code the type, an attribute, or state property to a literal value; they must not contain the following reserved characters: ? , * , # , & , + , ; , ( , )
  • handler: A Promise to a module that is called when a RouteDefinition is matched by a location; details in Route Definition Handlers.
  • patterns: Optional. A regular expression that a parameter must match to be valid. It’s not required, but it is recommended that the regular expression pattern is explicitly wrapped in the start-of-line character (^) and end-of-line-character ($). If you don't use the start-of-line or end-of-line character, the pattern could allow unexpected values. Example: {"patterns": {"parameter": "^id[0-9a-zA-Z]{5}$"}}
  • exact: Optional. Default = true). If there is a nested router, this value must be false.
  • metadata: Optional. Developer-defined metadata. You can use the metadata property to attach arbitrary additional information to a RouteDefinition. By default, metadata can contain string keys and any type values; you can extend RouteDefinition for required or more strongly typed metadata.

Location information passed to RouteHandler.update(). The route handler receives a RouteInstance when a new client-side navigation event occurs. The route handler uses the RouteInstance to determine which view to show.

You use a RouterConfig object with createRouter() to provide information about the routes for which you want a router. A RouterConfig object has the following form:

  • routes: Optional. An array of route definitions. If no RouteDefinition array is supplied, the default is an empty array. Note that if you provide no value for RouterConfig.routes, the router can’t match any locations.
  • basePath: Optional. String. A path prefix applied to all URIs for the router. If no base path is provided, default = “”
  • caseSensitive: Optional. Boolean. If true, URIs are processed case sensitively. Default = false.

A route destination is provided by RouteHandler.new() via a callback.

  • viewset: A ViewSet object specifying the view to display.

The view to display. You supply a ViewSet object to a RouteDestination.

  • viewName: String. Information about the view to display. If unspecified: default. The viewName key maps to the viewName property for the outlet. Read on for more about viewName.

Because view sets can vary in complexity, the viewName property is flexible and allows you to provide either:

  • a Promise to a route handler module: { default: import('my/routeHandler') }

or

  • a ViewInfo object containing both:
    • A Promise to a route handler module.
    • A string specifier for the module. For example: "my/recipePage". Because the specifier for the module cannot be gleaned from a Promise alone, the specifier property informs the route handler of the string specifier for the module. Simple or straightforward use cases typically don’t require a specifier.
    • ViewInfo example: { default: { module: import('my/routeHandler'), specifier: 'my/routeHandler' } }

Finally, in a more advanced case, where a single page reference maps to more than one view, you can have more than one outlet, each outputting a different view component: { default: import('my/routeHandler'), sidebar: import('my/sideHandler'), footer: import('my/footerHandler') }

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

The following methods are used in client-side routing:

Creates and initializes a router.

  • config: Optional. Pass the RouterConfig object for which you want to create a router. If you don’t pass a RouterConfig to createRouter(), LWR creates a router with default values.

It is not typical to pass no RouterConfig to createRouter(). All RouterConfig properties are optional, but if you don't pass any value for RouterConfig.routes, the router can't match any locations.

A router object that you can pass to a router container component.

A router is a piece of code that manages client-side navigation changes. All navigation events flow through a router for processing. Use the createRouter() API to initialize an LWR router.

In order to use a router in an application, it must be attached to the DOM. This is done with a router container, provided by the lwr-router-container component. After you create and initialize the router, you receive a router object that can be passed to a router container.

“Create a Router” in Simple Client-Side Routing provides an example of using createRouter().

Generates a URL for a specified page reference.

  • context: The ID of the parent router container nearest to the component that is generating a URL. A ContextId is an opaque value that you obtain from the NavigationContext wire.

  • pageReference: The PageReference value for which to generate a URL.

A string with the generated URL for the given route or null if pageReference is invalid. If context is invalid, generateUrl() fails.

The “Navigate” section in Simple Client-Side Routing contains an example of using generateUrl().

Navigates to a page reference programmatically.

  • context: The ID of the parent router container nearest to the component that is generating a URL. A ContextId is an opaque value that you obtain from the NavigationContext wire.
  • pageReference: The PageReference value for the page to which to navigate.
  • replace: Optional. Boolean. If true, navigate() modifies the current history entry, replacing it with the location passed in the method parameters.

None.

The “Navigate” section in Simple Client-Side Routing contains an example of using navigate().

When you create a route definition, it includes a promise in its handler property to a route handler module that’s called when a location matches that RouteDefinition.

Route handler modules determine the associated "view" — the component to display — when the application navigates to a location. Route handler modules allow you to define enhanced routing rules such as branching and pivoting logic based on data and metadata values.

You implement the RouteHandler class interface to control the component displayed when your application navigates to a location. When a router matches an incoming location to a RouteDefinition, it accesses RouteDefinition.handler to determine the component to display.

The following methods are for RouteHandler:

  • new() Create a new route handler.
  • dispose() Clean up a route handler.
  • update() Provide a set of views for the current location.

Create a route handler.

  • callback: A reference to a route handler callback. Each RouteHandlerCallback takes a RouteDestination object: type RouteHandlerCallback = (routeDestination: RouteDestination) => void;. Make sure to save the reference to this callback. RouteHandler.update invokes the callback to return the ViewSet for a given RouteDestination / PageReference.

None.

Your router constructs a new route handler to determine the view for a location while processing a navigation event. The route handler uses the current location to determine the corresponding view or views. The route handler is active upon construction and can immediately emit results to the callback via update().

When using new(), make sure to save a reference to the route handler callback you receive. RouteHandler Example Implementation provides an example of using new() along with update().

Clean up a route handler.

  • None

None.

RouteHandler.dispose is called when a route handler is no longer needed by the app and no longer emits view changes. A disposed route handler cannot be used again.

Provide a set of views for the current location.

  • routeInfo: A RouteInstance providing location information. The route handler receives a RouteInstance when a new client-side navigation event occurs and uses it to determine which view to show.

None.

LWR calls RouteHandler.update whenever there's a navigation event. The route handler is responsible for calculating the updated view or views. It invokes the route handler callback obtained via RouteHandler.new to return the ViewSet for a given RouteDestination / PageReference.

This example implements the RouteHandler interface. For additional examples: Simple Client-Side Routing.

You can use NavigationMixin instead of the navigate() and generateUrl() APIs. Both options offer the same functionality, but only NavigationMixin is compatible with Lightning Experience. So if you're writing a component for use in both LWR and Lightning Experience, choose NavigationMixin.

The lwr/navigation module provides wire adapters from which Lightning web components can receive information about navigation events.

The following wires are available for client-side routing:

Get the current page reference from the router container.

This wire is also available in Lightning Experience.

  • none

A PageReference object containing reference to the current page.

Get a reference to the current view Lightning web component. An outlet uses the CurrentView wire to get the current view component, then displays it in the DOM.

  • viewName: Optional. Falls back to "default" if unspecified.

A reference to the current view Lightning web component.

In this snippet, the lwc:dynamic directive uses the view component constructor returned by CurrentView to display the current view component.

lwc:dynamic is only available on LWC Open Source, not on the Salesforce Platform.

Get a reference to a Lightning web component's navigation context (its closest ancestor router container), for use with the generateUrl() and navigate() APIs.

  • none

A ContextId. An opaque ID for the parent router container nearest to the component.

This example uses the NavigationContext wire to obtain a ContextId value. It assigns the ID to the navContext property, where it can be used by the navigate() API.

The following Lightning web components are available for LWR client-side routing:

In order to use a router in an application, it must be attached to the DOM. This is done with a router container, provided by the lwr-router-container Lightning web component.

A router container component has the following properties:

  • router: Optional. A router object obtained from the createRouter() API.

  • history-disabled: Optional. Boolean. Set history-disabled to true if your router container should not alter the browser's History API during navigation events/location changes.

The router container component fires these events:

  • onhandlenavigation: Dispatched when navigate(pageRef) is called. event.preventDefault() cancels the navigation event; event.detail is the PageReference.

  • onprenavigate: Dispatched when a navigation event is received and a RouteDefinition match is found. event.preventDefault() cancels the navigation event; event.detail is a RouteChange object.

  • onpostnavigate: Dispatched when a navigation event has completed; event.detail is a DomRoutingMatch object for the current location.
  • onerrornavigate: Dispatched when there's an error processing a navigation event (for example, no RouteDefinition match, prenavigate cancellation). event.detail is a MessageObject.

A router container provides “navigation context”. This means that the router container is responsible for processing all navigation wires and events from its descendants in the DOM (my-nav and lwr-outlet in the example code following).

“Create a Router” in Simple Client-Side Routing provides another example of attaching a router to a router container.

A router container can optionally have a single nested child router. Each parent router is responsible for processing the navigation events from its descendants.

Every RouteDefinition resolving to a view component that includes a child router must set exact to false:

Nested Client-Side Routing provides an example of a router container with a nested child router.

A view is the component to display when the application navigates to a location. It’s an outlet's job to dynamically render those view components. The lwr-outlet Lightning web component uses the CurrentView wire [LINK] to get the current view component, then displays it in the DOM.

An outlet component has the following properties:

  • view-name: Optional. The key of the ViewSet entry to display; default value is "default".

  • refocus-off: Optional. Boolean. Refocusing is on by default for accessibility. If refocus-off is present, the outlet does not put the browser focus on the view component when it loads.

An outlet component fires these events:

  • onviewchange: A handler for the viewchange event that is dispatched whenever the view component changes; event.detail is the view component class.
  • onviewerror: A handler for the viewerror event that is dispatched whenever the view component cannot be rendered; event.detail is the error and stack.

An outlet component has the following slot:

  • "error": The contents of the error slot to display if the view component fails to mount.

An outlet dynamically renders view components. The lwr-outlet Lightning web component uses the CurrentView wire to get the current view component, then displays it in the DOM.

This process is naturally lazy. The view isn’t imported until the outlet consumes it, so that any unused views aren’t fetched. When the router's view state changes, the outlet is flagged to display the view. At that point, if the view module hasn't already been fetched there may be an async process to retrieve it.

It's possible for a route definition handler to return multiple views, as in this example:

You can use multiple outlets to display all the current view components by setting different view-name values: