This answer uses LWR and Client-Side Routing.
Create a wildcard route in your lwr.config.json like so:
...
routes: [
{
"id": "appRoot",
"path": "/*",
"rootComponent": "app/router",
"layoutTemplate": "$layoutsDir/index.html"
}
]
...
Assuming your router is in your "app/router" module.
import routes from 'app/routes';
import { createRouter } from 'lwr/router';
import { LightningElement } from 'lwc';
export default class Router extends LightningElement {
router = createRouter({ routes });
}
Personally, I like storing my routes in a different module, app/routes:
import RouterHandler from 'app/routerHandler';
const routes = [
{
id: 'appRoot',
uri: '/',
handler: () => RouterHandler('page/home'),
page: { type: 'appRoot' },
},
{
id: 'demo1',
uri: '/demo1',
handler: () => RouterHandler('page/demo1'),
page: { type: 'demo1' },
},
{
id: 'demo2',
uri: '/demo2',
handler: () => RouterHandler('page/demo2'),
page: { type: 'demo2' },
},
{
id: 'nestedExample',
uri: '/nested',
handler: () => RouterHandler('nested/router'),
page: { type: 'nested' },
exact: false,
},
];
export default routes;
I have my app/routerHandler
configured like so:
export default function RouterHandler(module) {
class _RouterHandler {
callback;
constructor(callback) {
this.callback = callback;
}
dispose() {}
update() {
this.callback({
viewset: {
default: () => import(module),
},
});
}
}
return { default: _RouterHandler };
}
Of course, you can override it on a per module basis, but I use that for about 80% of my routes anyway.
Then what I do for the actual navigating is use a app/routerLink
module:
import routes from 'app/routes';
import { api, wire, LightningElement } from 'lwc';
import { navigate, NavigationContext } from 'lwr/navigation';
export default class RouterLink extends LightningElement {
@api href;
@api target;
@api title;
@api atts = {};
@api type;
@wire(NavigationContext) navContext;
handleClick(event) {
let route = routes.find((x) => x.uri === this.href);
if (route) {
event.preventDefault();
const pageOptions = route.page;
if (this.type) {
pageOptions.type = this.type;
}
if (this.atts) {
pageOptions.attributes = this.atts;
}
navigate(this.navContext, pageOptions);
}
}
}
Its html template:
<template>
<a
href={href}
title={title}
target={target}
onclick={handleClick}
>
<slot></slot>
</a>
</template>
By using an anchor, I can still use this component for routes not inside my app as when the href is not found in the routes, it will fallback to not running event.preventDefault and just use the native callback.
My app/routerLink
will also work for nested routes as it also checks for the "exact" property, which I won't elaborate but just have the module point to a secondary router.