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!


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() {
       // 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
            '/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 = () => {


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

    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) {

  • Thanks John. When 'setPage' function is called there is an error: DOMException: Failed to execute 'attachShadow' on 'Element'. The component argument seems to be ok. Do you know why this happens? Thanks! – Sebas Belaustegui Jun 21 '19 at 14:46
  • It sounds like maybe the element that you're trying to append to doesn't exist. You should try updating const container = this.template.querySelector('.container'); so it looks for an element in your template or add a div with a class of 'container' to your template. – John Towers Jun 21 '19 at 14:53
  • Yes, I did it, but it fails on the first line. On the 'createElement' call, before the query. – Sebas Belaustegui Jun 21 '19 at 14:55
  • 1
    I fixed it! The problem was a bad tagName. Thanks! – Sebas Belaustegui Jun 21 '19 at 15:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.