Skip to content

API RFC #1

@blittle

Description

@blittle

API Proposal

Requirements

  1. Declarative route definition
  2. Nested routes
  3. Server side rendering
  4. Code splits

Examples

<!--- Root app.html --->
<lwce-router>
  <ul>
    <li><lwce-link href="/">Home</lwce-link></li>
    <li><lwce-link href="/settings">Settings</lwce-link></li>
    <li><lwce-link href="/users/1234">Profile</lwce-link></li>
  </ul>
  <lwce-route path="/">
    <commerce-home></commerce-home>
  </lwce-route>
  <lwce-route path="/cart">
    <commerce-cart></commerce-cart>
  </lwce-route>
  <lwce-route path="/products/:productId">
    <commerce-product-details></commerce-product-details>
  </lwce-route>
</lwce-router>

<!--- Inside cart.html --->
<div>
  <lwce-link href="checkout">Checkout</lwce-link>
  <lwce-route path="">
    <commerce-cart-details></commerce-cart-details>
  </lwce-route>
  <lwce-route path="checkout">
    <commerce-cart-checkout></commerce-cart-checkout>
  </lwce-route>
</div>

Accessing route parameters:

import { LightningElement, wire } from 'lwc';
import { routeParams } from '@lwce/router';

export default class ProductDetails extends LightningElement {
  
  @wire(routeParams, {})
  params = {}
}
<template>
  <div>Details for {params.productId}</div>
</template>

Web component prior art

https://vaadin.com/router

  • Imperative
  • Route params are automatically set as a property on the component being mounted by the router
  • Lazy loading bundles also defined in the router declaration
  • Animation support with classes added and removed from the outer element the router mounts into

Example:

const router = new Router(document.getElementById('outlet'));
router.setRoutes([
    {path: '/', component: 'x-home-view'},
    {
      path: '/user/:id',
      bundle: '/user.bundle.js',
      component: 'x-user-js-bundle-view' // <-- defined in the bundle
    },
  ]);

https://www.webcomponents.org/element/@appnest/web-router

  • Imperative/declarative - must query the dom for the "router slot" element and call a method on the element to add routes.
  • The component definition can be a reference to a component class, a component instance, or a factory function which returns a promise that resolves to one of the prior (lazy loading).
  • Includes a "router link" component that builds an anchor tag and sets an "active" attribute when the path is active.
  • A setup method is available in the route definition which will run before the component mounts, allowing you to set properties on the component, including route params.
  • Or it provides a function for you to call within the mounted component that will give you params if the component is active
  • No nested support

Example:

// add to your html:     <router-slot></router-slot>

const routerSlot = document.querySelector("router-slot");
await routerSlot.add([
  {
    path: "login",
    component: () => import("./path/to/login/component") // Lazy loaded
  },
  {
    path: "users/:userId",
    setup: (component: UserComponent, info: RoutingInfo) => {
      component.userId = info.match.params.userId;
    },
    component: UserComponent // Not lazy loaded
  }
]);

https://github.com/ryansolid/webcomponent-router

  • The API supports deep nesting and is intriguing, but uses the is= web component syntax
  • Routes are named and nested with . notation
  • Supports lazy loading

Example

const router = new Router(document.getElementById('main'));

router.map(r => {
  r.index(() => ['user', { userId: 123 }]);
  r.notFound(() => ['index']);
  r.route('user', { path: '/users/:userId', tag: 'pane-user' }, r => {
    r.index(() => ['sets']);
    r.route('sets', {
      path: '/sets', tag: 'pane-sets',
      // dynamic import for code splitting
      onEnter: () => import("../components/pane-sets")
    });
    r.route('set', { path: '/sets/:setId', tag: 'pane-set' });
  });
});

router.start();

router.transitionTo('user.set', {setId: 49});

Non-web component prior art

Some have route components along with link components. Reach router is interesting in that there is no "route" component, all children of a router component take a prop of "path" and the router could mount any of these components based upon the URL state. This mean "routes" are just any old component.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions