
Boring Router
A light-weight, type-safe, yet reactive router service using MobX.
Why
There are multiple reasons pushing me to write Boring Router instead of sticking with React Router.
- Making React Router work with a global store/service is not straightforward. Having to parse location twice with the same schema defined with two different approaches looks redundant to me.
- React Router (and many other alternatives) provides basically no type safety between path schema and match result. And you have to write
to
as strings in links. - People can easily write ambiguous path schema without it bringing them attention.
- I think I've got a nice idea. 😉
Installation
yarn add history react mobx mobx-react
yarn add boring-router
Usage
import {HistoryProvider, Link, Route, RouteMatch, Router} from 'boring-router';
import {observer} from 'mobx-react';
import {createBrowserHistory} from 'history';
import React, {Component} from 'react';
const history = createBrowserHistory();
const router = Router.create(
{
account: true,
about: true,
notFound: {
$match: RouteMatch.rest,
},
},
history,
);
@observer
class App extends Component {
render() {
return (
<HistoryProvider value={history}>
<Route match={router.account}>
Account page
<hr />
<Link to={router.about}>About</Link>
</Route>
<Route match={router.about}>About page</Route>
<Route match={router.notFound}>Not found</Route>
</HistoryProvider>
);
}
}
Schema
Boring Router defines routes via a tree-structure schema:
type RouteSchemaDict = Dict<RouteSchema | boolean>;
interface RouteSchema {
$match?: string | RegExp;
$query?: Dict<boolean>;
$children?: Dict<RouteSchema | boolean>;
}
Two pre-defined $match
regular expression is available as RouteMatch.fragment
(/[^/]+/
) and RouteMatch.rest
(/.+/
).
Route match
The value of expression like router.account
in the usage example above is a RouteMatch
, and it has the following reactive properties and methods:
interface RouteMatch<TParamDict> {
$matched: boolean;
$exact: boolean;
$params: TParamDict;
$ref(params?: Partial<TParamDict>, preserveQuery?: boolean): string;
}
Examples
Example list
-
Basic
Basic usage.
<Route match={router.account}>
<p>Account page</p>
</Route>
-
Exact
Match exact path.
<Route match={router.account} exact>
<p>Exact account page</p>
</Route>
-
Fragment
Boring Router's version of /account/:id
alike parameter.
<Link to={router.account.id} params={{id: '123'}}>
Account 123
</Link>
<Route match={router.account.id}>
<p>Account {router.account.id.$params.id} details page</p>
</Route>
-
Query
Handle query string parameter.
<Link to={router.account} params={{id: '123'}}>
Account 123
</Link>
<Route match={router.account}>
<p>Account {router.account.$params.id} details page</p>
</Route>
-
Route Component
Use <Route />
with a route component.
<Route match={router.account} component={AccountPage} />
<Route match={match.details}>
<p>Account {match.$params.id} details page</p>
</Route>
-
Multiple Route Match
Match with multiple route match for shared content.
<Route
match={[router.account.signUp, router.account.resetPassword]}
component={AccountPage}
/>
Run an example
yarn install
yarn build
yarn global add parcel
parcel examples/[name]/index.html
License
MIT License.