Routing

Routing in your components is possible via @webcomp/router module.

WebComp router is a modern client side router that provides Express-like route handling, supports history API and hash mode and provides some higher order helpers. Let's go through everything.

Basic usage

Here's how you can define routes in your component:

import { WebComponent } from '@webcomp/core';
import { Router } from '@webcomp/router';

class MyElement extends WebComponent{
  componentDidMount = () => {
    Router.on('/profile/:id', this.handleRouteChange);
  }

  handleRouteChange = (url) => {
    console.log('Route changed! ID:', url.params.id); // "Route changed! ID: 123"
  }

  changeRoute = () => {
    Router.push('/profile/123');
  }

  render() {
    return <button onClick={this.changeRoute}>Change Route</button>
  }
}

Your route handler function will receive a url object with the following structure:

  • params - object with url params

  • path - Full path string

  • query - Parsed object from query string

Router Methods and Properties

Besides push and on that we've already seen, router provides a few additional methods and configuration options.

Router.mode

  • Default: hash

A string indicating router's operation mode. Can be hash or history. To configure router for a different mode, assign a new value to it:

Router.mode = 'history';

Keep in mind that this will configure the entire router, so you should only do it once at the top level of your app (before you register any of your components).

Router.root

  • Default: /

A string indicating router's root path. Same as with mode, you can assign a new value to set it:

Router.root = '/settings';

This should also be configured once at the top level of your app.

`on(route, handlerFunction, persist)`

Define a new handler for a route. Optional persist argument will prevent a handler from flushing if Router.flushHandlers is called.

Calling Router.on will return a string ID which you can use to remove handlers later if needed. Usage:

Router.on('/profile/:id', (url) => {
  console.log(url.params.id);
})

removeHandler(id)

Removes previously defined route handler by it's ID. Usage:

Router.removeHandler('_mp6vmcfda');

flushHandlers()

Removes all handlers from the router except ones that were defined with persist flag.

dangerouslyFlushRouter()

Completely resets router to it's default state. Root is set to '/', mode to 'hash' and all route handlers are removed (even persisted ones).

You will most likely never need to use this method. If you must, use with caution.

push(path)

Navigate to a new route. Uses `pushState` when in `history` mode. Usage:

Router.push('/path/to/something');

replace(path)

Navigate to a new route and replace the history state instead of pushing. Note: Only available in history mode.

Routerize decorator

Defining routes explicitly isn't always convenient. Some times you just want to watch what happens and do stuff based on that. For these use cases, WebComp provides a @routerize decorator:

import { WebComponent } from '@webcomp/core';
import { routerize } from '@webcomp/router';

@routerize
class MyElement extends WebComponent{
  render(props) {
    // props.router.state is the same object that is passed to route handlers
    return <h1>Hello route {props.router.state.path}</h1>
  }
}

Here's the structure of this.props.router prop that @routerize gives you:

{
  on(),
  push()
  replace(),
  flushHandlers(),
  dangerouslyFlushRouter(),
  root,
  state: {
    path,
    params: {},
    query: {},
  },
}

This will be updated on every route change, so you can rely on the prop directly, without needing to specify route handlers.

Note: Due to how router works by default, your component will be re-rendered on mount when routerized. It's usually not a big deal, but if it is, you should use a custom router (see below) with a skipInitial option.

<Route /> Component

If you only need to watch routes to change your rendered content, you can use <Route /> component to do that:

import { WebComponent } from '@webcomp/core';
import { Route } from '@webcomp/router';

class MyElement extends WebComponent{
  render(props) {
    return (
      <div>
        <Route path="/">
          <h1>Home Page</h1>
        </Route>
        <Route path="/settings">
          <h1>Settings</h1>
        </Route>
      </div>
    )
  }
}

Shallow routes

Besides child node, Route also supports passing a `component` prop for render. If you do this, rendered component will automatically be routerized. You can bypass this behavior by using shallow prop.

import { WebComponent } from '@webcomp/core';
import { Route } from '@webcomp/router';
import FancyHeader from './FancyHeader';

class MyElement extends WebComponent{
  render(props) {
    return (
      <div>
        <Route path="/" component={FancyHeader} />
        <Route path="/settings" component={FancyHeader} shallow />
      </div>
    )
  }
}

In the above example, FancyHeader component on route / will get this.props.router, but the one on /settings route won't.

Advanced Usage

Custom Routers

In some cases you need to have different router configurations for different components. WebComp allows you to create custom routers for this.

Default Router is an instance of a WCRouter class, which you can reuse or extend.

import { WCRouter } from '@webcomp/router';

class SafeRouter extends WCRouter {
  constructor(options) {
    super(options);

    this.dangerouslyFlushRouter = () => throw new Error("Safe router doesn't allow flushing");
    this.flushHandlers = () => throw new Error("Safe router doesn't allow flushing");
  }
}

const MyCustomRouter = new SafeRouter({ skipInitial: true, mode: 'history' });

Custom router options

  • skipInitial - If this is set, router won't fire handlers on initial page load.

  • mode - Set default mode. Can be 'hash' or 'history'.

  • root - Set custom default root

Routerizing with custom router

The @routerize decorator uses default router instance. If you want to routerize your component with your custom router, you can use routerizeWith decorator instead:

import { WebComponent } from '@webcomp/core';
import { WCRouter, routerizeWith } from '@webcomp/router';

const CustomRouter = new WCRouter({ skipInitial: true });

@routerizeWith(CustomRouter)
class MyElement extends WebComponent{
  render(props) {
    return <h1>...</h1>
  }
}

Using custom routers with <Route />

You can also use custom routers with <Route /> component by providing it as a customRouter prop:

import { WebComponent } from '@webcomp/core';
import { WCRouter, Route } from '@webcomp/router';
import FancyHeader from './FancyHeader';

const MyRouter = new WCRouter({ skipInitial: true });

class MyElement extends WebComponent{
  render(props) {
    return (
      <div>
        <Route path="/" component={FancyHeader} customRouter={MyRouter} />
      </div>
    )
  }
}

Last updated