Q:

LWC - Best practice for routing?

I would like to know the best practice for routing in LWC. I mean, how to show a specific component on a specific URL, how to go to another route from a component, etc. If you can show me an example I would be grateful.
Thanks!

A:

There's a demo up on GitHub from the TDX session on using LWC outside of Salesforce.
That uses Navigo for routing in the app module. Here's a slimmed down version of the routing from that example with some comments from my own experience trying to implement this:
import { LightningElement, createElement, track } from 'lwc';
import Navigo from 'navigo'; // import Navigo

export default class App extends LightningElement {

    // instantiate the router class
    router = new Navigo(location.origin, false);

    constructor() {
        super();
       // define your routes. The await functions are for use with chunking but you could remove them and just do a normal import if all of your scripts are loaded at once
        this.router.on({
            '/podcasts': async () => {
                const { default: ViewPodcasts } = await import(/* webpackChunkName: "view-podcasts" */ 'view/podcasts');
                this.setPage('view-podcasts', ViewPodcasts);
            },
            '/podcasts/:id': async ({ id }) => {
                const { default: ViewPodcast } = await import(/* webpackChunkName: "view-podcast" */ 'view/podcast');
                this.setPage('view-podcast', ViewPodcast, {
                    podcastId: parseInt(id, 10),
                });
            },
            '/discover': async () => {
                const { default: ViewDiscover } = await import(/* webpackChunkName: "view-discover" */ 'view/discover');
                this.setPage('view-discover', ViewDiscover);
            },
            '/categories/:id': async ({ id }) => {
                const { default: PodcastList } = await import(/* webpackChunkName: "view-category" */ 'view/category');
                this.setPage('view-category', PodcastList, {
                    categoryId: parseInt(id, 10),
                });
            },
        });

        const navigateToDefault = () => {
            this.router.navigate('/podcasts');
        };

        this.router.notFound(navigateToDefault);
        this.router.on(navigateToDefault);
    }

    renderedCallback() {
        // Resolve the current view only after the container has rendered
        if (!this.isRendered) {
            this.isRendered = true;
            this.router.resolve();
        }
    }

    setPage(tagName, component, props = {}) {
     // when a route is called, create the related component and insert it into the DOM in whatever container you want
        const el = createElement(tagName, {
            is: component,
            fallback: false,
        });

        Object.assign(el, props);

        // Remove previous components from the container if necessary
        const container = this.template.querySelector('.container');
        while (container.firstChild) {
            container.removeChild(container.firstChild);
        }

        container.appendChild(el);
    }
}

