# Read Me

[![npm version](https://badge.fury.io/js/router5.svg)](http://badge.fury.io/js/router5) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Build Status](https://travis-ci.org/router5/router5.svg)](https://travis-ci.org/router5/router5) [![Join the chat at https://gitter.im/router5/router5](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/router5/router5?utm_source=badge\&utm_medium=badge\&utm_campaign=pr-badge\&utm_content=badge) [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)

> Official website: [router5.js.org](https://router5.js.org)

router5 is a **framework and view library agnostic router**.

* **view / state separation**: router5 processes routing **instructions** and outputs **state** updates.
* **universal**: works client-side and server-side
* **simple**: define your routes, start to listen to route changes
* **flexible**: you have control over transitions and what happens on transitions

```javascript
import createRouter from 'router5'
import browserPlugin from 'router5-plugin-browser'

const routes = [
  { name: 'home', path: '/' },
  { name: 'profile', path: '/profile' }
]

const router = createRouter(routes)

router.usePlugin(browserPlugin())

router.start()
```

**With React (hooks)**

```javascript
import React from 'react'
import ReactDOM from 'react-dom'
import { RouterProvider, useRoute } from 'react-router5'

function App() {
  const { route } = useRoute()

  if (!route) {
    return null
  }

  if (route.name === 'home') {
    return <h1>Home</h1>
  }

  if (route.name === 'profile') {
    return <h1>Profile</h1>
  }
}

ReactDOM.render(
  <RouterProvider router={router}>
    <App />
  </RouterProvider>,
  document.getElementById('root')
)
```

**With observables**

Your router instance is compatible with most observable libraries.

```javascript
import { from } from 'rxjs/observable/from'

from(router).map(({ route }) => {
  /* happy routing */
})
```

## Examples

* With React: [`code`](https://github.com/router5/router5/tree/35f98f3ff7701e6d23d1079a048d0af96962fe75/examples/react/README.md) | [`live`](https://codesandbox.io/s/github/router5/router5/tree/master/examples/react)

## Docs

* Introduction
  * [Why router5?](https://router5.js.org/introduction/why-router5)
  * [Getting Started](https://router5.js.org/introduction/getting-started)
  * [Ecosystem](https://router5.js.org/introduction/ecosystem)
  * [Core concepts](https://router5.js.org/introduction/core-concepts)
  * [Transition phase](https://router5.js.org/introduction/transition-phase)
* Guides
  * [Defining routes](https://router5.js.org/guides/defining-routes)
  * [Path Syntax](https://router5.js.org/guides/path-syntax)
  * [Router options](https://router5.js.org/guides/router-options)
  * [Navigating](https://router5.js.org/guides/navigating)
  * [In the browser](https://router5.js.org/guides/in-the-browser)
  * [Observing state](https://router5.js.org/guides/observing-state)
* Integration
  * [With React](https://router5.js.org/integration/with-react)
  * [With Redux](https://router5.js.org/integration/with-redux)
* Advanced
  * [Plugins](https://router5.js.org/advanced/plugins)
  * [Middleware](https://router5.js.org/advanced/middleware)
  * [Preventing navigation](https://router5.js.org/advanced/preventing-navigation)
  * [Errors and redirections](https://router5.js.org/advanced/errors-and-redirections)
  * [Dependency injection](https://router5.js.org/advanced/dependency-injection)
  * [Loading async data](https://router5.js.org/advanced/loading-async-data)
  * [Universal routing](https://router5.js.org/advanced/universal-routing)
  * [Listeners plugin](https://router5.js.org/advanced/listeners-plugin)
* [API Reference](https://router5.js.org/api-reference)


# Introduction


# Why router5?

`router5` is part of a new generation of routers: instead of rendering "views" or "pages", router5 outputs its state. The main idea behind router5 is to treat routing state like any other application data or state.

"Traditional" routing has been heavily influenced by server-side routing, which is stateless, while client-side routing is stateful. For more in-depth description of router5, look at [Understanding router5](/introduction/core-concepts).

## What is router5 best suited for?

Router 5 is best suited for component-based architectures, where components can easily be composed together. It works best with React, Preact, Inferno, Cycle.js, etc...

It also works very well with state containers like [Redux](http://redux.js.org/): your state container is placed between your view and your router, and your view subscribes to state updates (rather than directly subscribing to route updates).

See available integrations:

* [With React](/integration/with-react)
* [With Redux](/integration/with-redux)

## ReactiveConf 2016 talk

Watch my talk at ReactiveConf 2016: "Past and future of client-side routing", it gives a great overview of what routing is, and what router5 does:

{% embed url="<https://www.youtube.com/watch?v=hblXdstrAg0>" %}


# Getting Started

*router5* is available in all major formats: **ES6**, **CommonJS**, and **UMD**. It can be installed using **npm** or **yarn**. Alternatively, you can download a specific version from [github](https://github.com/router5/router5/releases).

## Installation

```
# yarn
yarn add router5
# npm
npm install router5
```

## Include router5 in your application

### ES2015 syntax

```javascript
// ES2015+
import createRouter from 'router5';

import browserPlugin from 'router5-plugin-browser';
import persistentParamsPlugin from 'router5-plugin-persistent-params';
```

### CommonJS syntax

```javascript
var createRouter = require('router5').default;
var browserPlugin = require('router5-plugin-browser');
var persistentParamsPlugin = require('router5-plugin-persistent-params');
```

### UMD

Various UMD bundles are accessible under `/dist/umd`: you should use them for AMD or global. The router5 bundle contains all *router5* dependencies (*route-node* and *path-parser*), but doesn't contain plugins.


# Ecosystem

## Provided packages

* [react-router5](https://github.com/router5/router5/tree/master/packages/react-router5) integration with react
* [redux-router5](https://github.com/router5/router5/tree/master/packages/redux-router5) integration with redux

## Community packages

* [mobx-router5](https://github.com/LeonardoGentile/mobx-router5): integration with MobX
* [react-mobx-router5](https://github.com/LeonardoGentile/react-mobx-router5): integration with Mobx and React
* [marko-router5](https://jesse1983.github.io/marko-router5/#/): integration with MarkoJS
* [mr-router5](https://github.com/pzmosquito/mr-router5): lightweight integration with MobX and React

## Examples

* [With React](https://codesandbox.io/s/github/router5/router5/tree/master/examples/react)

## Not up to date

* [rxjs-router5](https://github.com/router5/router5/tree/master/packages/redux-router5) integration with rxjs
* [xstream-router5](https://github.com/router5/router5/tree/master/packages/redux-router5) integration with xstream
* [deku-router5](https://github.com/router5/router5/tree/master/packages/deku-router5) integration with deku
* [router5-link-interceptor](https://github.com/jas-chen/router5-link-interceptor) link interceptor
* [router5-boilerplate](https://github.com/sitepack/router5-boilerplate)
* [universal-react-redux-hapi](https://github.com/nanopx/universal-react-redux-hapi)


# Core concepts

The main idea behind router5 is to treat routes like any other application data / state. This guide aims to take you through router5's key concepts.

In "traditional" routing, a specific route is associated with a *route handler*. Such handlers would return your application tree or would link your route to a specific view / component. With router5 it is reversed: rather than the router updating the view, it is up to the view to listen / bind / subscribe to route changes in order to update itself.

## The state

[router5](https://github.com/router5/router5) is the core repository. Once you have defined your routes and started your router, it only does one thing: it takes navigation instructions and output state updates.

![Router](/files/-LDCQOvDsCxbtXPnW5_f)

Updating your browser history or listening to URL changes is considered a side-effect, because they are specific to an environment where your application might run (the browser). You can use the browser plugin to update the browser URL and translate popstate events to routing instructions.

A state object will contain:

* The `name` of the route
* The parameters (`params`) of the route
* The `path` of the route

### Tree of routes

Your routes are organised in a tree, made of segments and nodes. At the top will always be an unnamed root node (its name is an empty string `''`). It gives you the ability to have nested routes, each node of the tree (except the root node) is a valid route of your application.

For the rest of this article, we will use the following simple example of a few nested routes:

![Tree of routes](/files/-LDCQOvJonQb8NrPYG91)

### Transition

During a transition phase, the router will follow a **transition path**: it will deactivate some segments and activate some new ones. The intersection node between deactivated and activated segments is the **transition node**. The **transition node** is very important for your view, as we are about to discover.

Using the tree of routes shown above, let's consider we transition from `home` to `admin.users`: the transition node will be *the unamed root node*, we will deactivate `home` and activate `admin` and `admin.users`.

![Transition example #1](/files/-LDCQOvQJbm9MZhqzxFC)

Now let's see another example: a transition from `admin.roles` to `admin.users`. The transition node will be `admin`, and the admin segment will remain activated. `admin.roles` will be deactivated and `admin.users` will be activated.

![Transition example #1](/files/-LDCQOvTyGSwwm5L70Np)

## The view

> The router is unaware of your view and you need to bind your view to your router's state updates.

This is where you need to forget about route handlers and linking routes to components. On the left you have state updates coming from your router, and on the right you have your application view. Your application view is already in a certain state, and will now have to update to reflect the latest state updates.

On a route change, you only need to re-render a portion of your app. Depending on where you come from, for the same given route, a smaller or larger part of your application view will need to be re-rendered. This is why route handlers are not helpful: routing is not about mapping a route to a component, it is about going from A to B.

### Binding to route changes

Your view will need to subscribe to route updates, or specific route updates. There are three types of events you might want to react to, depending on what information you are after:

* The router has navigated to a route (any route)
* The router has navigated to a specified route
* A specified node is the transition node

The last point is the main one. We have seen your routes are organised in a tree. Your components are also organised in a tree, like DOM elements of a page are. In your application, each route node (when active) will have a corresponding component node. Those components should be re-rendered if their associated node is a transition node of a route change.

Below is an example of associated route and component nodes, when `admin.users` is active:

![Transition nodes](/files/-LDCQOv_5XABTXPsnug_)

The current route is `admin.users`. If we were to navigate to `home`, `Main` would be the component associated to the route node `''`. It would re-render to output a `Home` component instance rather than an `Admin` one, discarding the whole admin view.

> The **transition node** (as explained above), *is* the node to re-render your view from.

![Relation between router and view](/files/-LDCQOvcFNYXWQf9PEIA)

In slightly more complicated cases, you might have other parts of your screen to re-render. For example, you might have a header, a main menu or a side panel to update on a route change: in that case you can listen to any route change or a specific route change, and re-render that specific portion of a screen. Keep transition nodes for your "main" content update.


# Transition phase

The following flowchart illustrates a transition process between two states.

![Going from 'users.view' to 'orders.view'](/files/-LDCQPc4rcFIF9ZAXj_w)

![Transition flow chart](/files/-LDCQPc63MxW2oJMMvHG)


# Guides


# Defining routes

There are a few ways to add routes to your router. You can specify your routes when creating a router instance and / or use chainable `add` and `addNode` methods to add routes.

## With plain objects

You can define your routes using plain objects:

* `name`: the route name
* `path`: the route path, relative to its parent

Route objects optionally accept the following properties:

* `canActivate`: a method to control whether or not the route node can be activated (see [Preventing navigation](https://router5.js.org/advanced/preventing-navigation))
* `forwardTo`: if specified, the router will transition to the forwarded route instead. It is useful for defaulting to a child route
* `defaultParams`: an object of default params to extend when navigating and when matching a path
* `encodeParams(stateParams)`: a function of state params returning path params. Used when building a path given a route name and params (typically on start and navigation).
* `decodeParams(pathParams)`: a function of path params returning params. Used when matching a path to map path params to state params.

Note on `encodeParams` and `decodeParams`: one can't be used without another, and applying one after another should be equivalent to an identity function.

### Flat route list

You can define your routes using a flat list, in which case route names must be specified in full.

```javascript
const routes = [
    { name: 'users',      path: '/users'},
    { name: 'users.view', path: '/view'},
    { name: 'users.list', path: '/list'}
];
```

### Tree of routes

You can define your routes using a tree (making use of `children`), in which case route names are relative to their parent.

```javascript
const routes = [
    { name: 'users', path: '/users', children: [
        { name: 'view', path: '/view'},
        { name: 'list', path: '/list'}
    ]}
];
```

## Adding routes

You can add all your routes at once using `createRouter` or `router.add`.

### When creating your router

```javascript
const router = createRouter(routes, options);
```

### After creating your router

`.add()` accepts single or multiple nodes, flat or nested.

```javascript
myRouter.add({ name: 'about', path: '/about' });
// Or
myRouter.add([
    {name: 'about',   path: '/about'},
    {name: 'contact', path: '/contact'},
]);
```

## Configuring the root node path

At the top of your tree of routes, there is an unamed node called the root node. Its path is empty and can be configured using `router.setRootPath(path)`. It can be used for example to list a number of allowed query parameters for all routes in strict query parameters mode (`router.setRootPath('?param1&param2')`).


# Path Syntax

{% hint style="info" %}
*router5* uses [path-parser](https://github.com/troch/path-parser) for parsing, matching and generating URLs
{% endhint %}

## Defining parameters

Four parameter types are supported:

* `:param`: url parameters
* `;matrix`: matrix parameters
* `*splat`: for parameters spanning over multiple segments. Splat parameters are greedy and could swallow a

  large part of your URL. It is recommended to handle with care and to ONLY use on routes without children.
* `?param1&param2` or `?:param1&:param2`: query parameters

## Constrained parameters

Url and matrix parameters can be constrained with a regular expression. Backslashes need to be escaped.

* `:param<\\d+>` will match numbers only for parameter param
* `;id<[a-fA-F0-9]{8}>` will match 8 characters hexadecimal strings for parameter id

Constraints are also applied when building paths: when passing incorrect params to `.navigate()`, an error will be thrown.

## Absolute nested paths

You can define absolute nested paths (not concatenated with their parent's paths). Note that absolute paths are not allowed if any parent of a node has parameters.

```javascript
const router = createRouter([
    { name: 'admin', path: '/admin' },
    { name: 'admin.users', path: '~/users' }
]);

router.buildPath('admin.users'); // '/users'
```


# Router options

You can configure your router instance by passing options to the constructor or by using `.setOption(optName, optValue)`.

```javascript
var router = createRouter([], {
    allowNotFound: false,
    autoCleanUp: true,
    defaultRoute: 'home',
    defaultParams: {},
    queryParams: {
        arrayFormat: 'default',
        nullFormat: 'default',
        booleanFormat: 'default'
    },
    queryParamsMode: 'default',
    trailingSlashMode: 'default',
    strictTrailingSlash: false,
    caseSensitive: false,
    urlParamsEncoding: 'default'
})
```

## Default route

When your router instance starts, it will navigate to a default route if such route is defined and if it cannot match the URL against a known route:

* `defaultRoute`: the default route.
* `defaultParams`: the default route params (defaults to `{}`)

See [navigation guide](https://github.com/router5/router5/tree/1cc1c6969a96918deb28e45b8c5b2d6aa19d0a19/docs/navigation.html) for more information.

## Allow not found

There are two ways to deal with not found routes: the first one is to configure a `defaultRoute` (and `defaultParams`), the second one is to allow those not found routes to create a new routing state. Set `allowNotFound` to true and the router will emit a state value for unmatched paths.

For example, if you try to match `/hello-world` and you don't have this route defined, the router will emit the following state:

```javascript
import { constants } from 'router5';

const state = {
    name: constants.UNKNOWN_ROUTE
    params: { path: '/hello-world' },
    path: '/hello-world'
}
```

## URL parameters encoding and decoding

Option `urlParamsEncoding` controls the encoding and decoding of URL parameters, when matching and building paths. It supports the following values:

* `'default'`: `encodeURIComponent` and `decodeURIComponent` are used but some characters to encode and decode URL parameters, but some characters are preserved when encoding (sub-delimiters:`+`,`:`,`'`,`!`,`,`,`;`,`*`).
* `'uriComponent'`: use `encodeURIComponent` and `decodeURIComponent` for encoding and decoding URL parameters.
* `'uri'`: use `encodeURI` and `decodeURI` for encoding and decoding URL parameters.
* `'none'`: no encoding or decoding is performed
* `'legacy'`: the approach for version 5.x and below (no longer recommended to use)

## Query parameters mode

Option `queryParamsMode` can take the following values:

* `'default'`: a path will match with any query parameters added, but when building, extra parameters won't appear in the returned path.
* `'strict'`: a path with query parameters which were not listed in node definition will cause a match to be unsuccessful. When building, extra parameters won't appear in the returned path.
* `'loose'`: a path will match with any query parameters added, and when building, extra parameters will appear in the returned path.

## Query parameters formatting

You can specify how array, boolean and null values are formatted in query parameters, and how they are matched.

* `arrayFormat`: Specifies how arrays should be stringified
  * `'none'` (default): no brackets or indexes are added to query parameter names (`'role=member&role=admin'`)
  * `'brackets`: brackets are added to query parameter names (`'role[]=member&role[]=admin'`)
  * `'index'`: brackets and indexes are added to query parameter names (`'role[0]=member&role[1]=admin'`)
* `booleanFormat`: specifies how boolean values are stringified and parsed
  * `'none'` (default): booleans are stringified to strings (`'istrue=true&isfalse=false'`)
  * `'empty-true'`: same as `'none'` except true values are stringified without value (`'istrue&isfalse=false'`). If you choose this boolean format, make sure to change the value of `'nullFormat'`.
  * `'string'`: same as `'none'` but `'true'` and `'false'` are parsed as booleans
  * `'unicode'`: `true` and `false` are displayed with unicode characters, and parsed as booleans (`'istrue=✓&isfalse=✗'`)
* `nullFormat`: specifies how null values are stringified and parsed
  * `'default'` (default): null values are stringified without equal sign and value (`'isnull'`)
  * `'string'`: null values are stringified to `'null'` (`'isnull=null'`) and parsed as null values
  * `'hidden'`: null values are not stringified

## Trailing slash mode

Option `trailingSlashMode` can take the following values:

* `'default'`: building follows path definitions
* `'never'`: when building, trailing slash is removed
* `'always'`: when building, trailing slash is added

## Strict trailing slash

By default, the router is not in "strict match" mode. If you want trailing slashes to not be optional, you can set `strictTrailingSlash` to \`true\`\`.

## Automatic clean up

If `autoCleanUp` is set to true, the router will automatically clear `canDeactivate` functions / booleans when their associated segment becomes inactive.

## Case sensitivity

By default, matching of routes is case insensitive. You can set `caseSensitive` to `true` if you want to change that behaviour.


# Navigating

After configuring your routes, you need to enable navigation by starting your router instance.

## Starting your router

```javascript
const myRouter = createRouter([
    { name: 'home', path: '/home' },
    { name: 'about', path: '/about' },
    { name: 'contact', path: '/contact' }
]);

myRouter.start('/home');
```

> When using `.start()`, you should supply a starting path or state except if you use the browser plugin (the current URL will automatically be used).

Invoking the `.start(startPathOrState[, done])` function will:

* Attempt to navigate to `startPathOrState`
* Attempt to match the current URL if no `startPathOrState` was provided, or navigation failed
* Attempt to navigate to the default route if it could not match the provided start path or if `startPathOrState` was not provided / failed

And will:

* Enable navigation

Providing a starting state is designed to be used for universal JavaScript applications: see [universal applications](https://github.com/router5/router5/tree/1cc1c6969a96918deb28e45b8c5b2d6aa19d0a19/docs/guides/universal-applications.md).

## Defining a default route

A default route can be set in `createRouter` options. The following example will cause your application to navigate to `/about`:

```javascript
var myRouter = createRouter([
        { name: 'home', path: '/home' },
        { name: 'section', path: '/:section' }
    ], {
        defaultRoute: 'section'
        defaultParams: {section: 'about'}
    })
    .start(function (err, state) {
        /* ... */
    });
```

A callback can be passed to start and will be invoked once the router has transitioned to the default route.

## Navigating to a specific route

Router5 exposes the following method: `navigate(routeName, routeParams, opts)`. This method has to be called for navigating to a different route: **clicks on links won't be intercepted by the router**.

```javascript
myRouter.navigate('section', {section: 'contact'});
// Will navigate to '/contact'
```

### Forcing a reload

When trying to navigate to the current route nothing will happen unless `reload` is set to `true`.

```javascript
myRouter.navigate('section', {section: 'contact'}, {reload: true});
```

### Replacing current state

Set `replace` to true for replacing the current state in history when navigating to a new route. Default behaviour is to add an entry in history.

```javascript
myRouter.navigate('section', {section: 'contact'}, {replace: true});
```

### Custom options

You can pass any option to `navigate`: those options will be added to the state of your router (under `meta`).

### Navigate callback

Like for `.start()`, `.navigate()` accepts a callback (last argument):

```javascript
myRouter.navigate('route', function (err, state) {
    /* ... */
})
```

## Stopping your router

At any time you can stop (pause) a router and it will prevent any navigation. To resume, simply invoke `.start()` again.

```javascript
myRouter.stop();
```


# In the browser

The browser plugin will automatically update your browser URL and state on route changes. It will also listen to popstate events (triggered by back and forward buttons and manual URL changes).

## Using the browser plugin

This plugin uses HTML5 history API and therefore is not compatible with browsers which don't support it. Refer to [caniuse.com](http://caniuse.com/#search=history) for browser compatibility.

It adds a bunch of functions to work with full URLs: `router.buildUrl(routeName, routeParams)` and `router.matchUrl(url)`. It also decorates the start function so you don't have to supply any start path (it extracts it from the current URL).

```javascript
import browserPlugin from 'router5-plugin-browser'

const router = createRouter()

router.usePlugin(
    browserPlugin({
        useHash: true
    })
)

router.start()
```

## Plugin options

* `forceDeactivate`: default to `true`, meaning `canDeactivate` handlers won't get called on popstate events. It is not recommended to set it to `false`.
* `useHash`
* `hashPrefix`
* `base`: the base of your application (the part to add / preserve between your domain and your route paths).
* `preserveHash`: whether to preserve the initial hash value on page load (default to `true`, only if `useHash` is `false`)
* `mergeState`: whether to keep any value added in history state by a 3rd party or not (default to `false`)


# Observing state

From router5\@6.1.0 and onwards, your router instance is compatible with most observable libraries.

## Subscribing to state changes

You can subscribe to route changes using `router.subscribe()`, and will receive an object containing `route` and `previousRoute`.

## Observing state changes

Router instances are observables. You can use most stream libraries out there and create a stream from your router instance:

* RxJS (`Rx.Observable.from(router)`)
* xstream (`xs.fromObservable(router)`)
* most (`most.from(router)`)
* etc...


# Integration


# With React

## Installation

Install module \`react-router5:

```bash
yarn add react-router5
# or
npm install --save react-router5
```

## Demos and examples

[Codesandbox link](https://codesandbox.io/s/github/router5/router5/tree/master/examples/react)

## Provider

* **RouterProvider**: adds your router instance and router state in context.

```javascript
const AppWithRouter = (
  <RouterProvider router={router}>
    <App />
  </RouterProvider>
)
```

## Connecting components

You can connect your components using three different methods:

* Higher-order components: `withRouter`, `withRoute` and `routeNode`
* Render props: `Router`, `Route` and `RouteNode`
* Hooks: `useRouter`, `useRoute` and `useRouteNode`

|                          | HoC          | Render prop | Hook           |
| ------------------------ | ------------ | ----------- | -------------- |
| Use your router instance | `withRouter` | `Router`    | `useRouter`    |
| Connect to routing state | `withRoute`  | `Route`     | `useRoute`     |
| Connect to a route node  | `routeNode`  | `RouteNode` | `useRouteNode` |

## Link components

* **Link**: a component to render hyperlinks. For a full list of supported props, check the source! `Link` is `withRoute` and `Link` composed together
* **ConnectedLink**: same as `Link`, except it re-renders on a route changes.

```javascript
import React from 'react'
import { Link } from 'react-router5'

function Menu(props) {
  return (
    <nav>
      <Link routeName="home">Home</Link>

      <Link routeName="about">About</Link>
    </nav>
  )
}

export default Menu
```


# With Redux

> Note: if a Redux integration exists, you might not want to couple your router state to Redux. Anyway that's my advice avec a few years: bind yourself to the router directly.

## How to use

You have two ways to use redux-router5, depending on how you want to navigate:

* Using the router5 plugin (named `reduxPlugin`)
* Using the redux middleware (named `router5Middleware`)

In both cases, **use the provided reducer (**`router5Reducer`**).**

## Using the router plugin

If you choose to not use the middleware, you need to add `reduxPlugin` to your router. The plugin simply syncs the router state with redux. To navigate, you will need to invoke `router.navigate`. If you use React, you can use `BaseLink` from `react-router5`.

```javascript
import { reduxPlugin } from 'redux-router5'

// You need a router instance and a store instance
router.usePlugin(reduxPlugin(store.dispatch))
```

## Using the redux middleware

The redux middleware automatically adds the redux plugin to the provided router instance. It will convert a set of redux actions to routing instructions. The available action creators are:

* `navigateTo(routeName, routeParams = {}, routeOptions = {})`
* `cancelTransition()`
* `clearErrors()`
* `canActivate(routeName, true | false)`
* `canDeactivate(routeName, true | false)`

```javascript
import { actions } from 'redux-router5'
```

Use `router5Middleware` alongside your other middlewares:

```javascript
import { createStore, applyMiddleware } from 'redux'
import { router5Middleware } from 'redux-router5'

const createStoreWithMiddleware = applyMiddleware(router5Middleware(router))(
    createStore
)
```

## Reducer

This packages exposes a reducer (`router5Reducer`) that you can add to your application. It contains four properties:

* `route`
* `previousRoute`
* `transitionRoute` (the current transitioning route)
* `transitionError` (the last error which occured)

```javascript
import { combineReducers } from 'redux'
import { router5Reducer } from 'redux-router5'

const reducers = combineReducers({
    router: router5Reducer
    // ...add your other reducers
})
```

## Route node selector

{% hint style="info" %}
**In version 6.0.0,** `routeNodeSelector` **has been renamed to** `createRouteNodeSelector`. In order to use it efficiently, you need react-redux >= 4.4.0 to be able to perform per component instance memoization.
{% endhint %}

`createRouteNodeSelector` is a selector creator designed to be used on a route node and works with `connect` higher-order component from `react-redux`.

If your routes are nested, you'll have a few route nodes in your application. On each route change, not all components need to be re-rendered. `createRouteNodeSelector` will only output a new state value if the provided node is concerned by a route change.

```javascript
import { connect } from 'react-redux'
import { createRouteNodeSelector } from 'redux-router5'
import { Home, About, Contact } from './components'
import { startsWithSegment } from 'router5-helpers'

function Root({ route }) {
    const { params, name } = route
    const testRoute = startsWithSegment(name)

    if (testRoute('home')) {
        return <Home params={params} />
    } else if (testRoute('about')) {
        return <About params={params} />
    } else if (testRoute('contact')) {
        return <Contact params={params} />
    }

    return null
}

export default connect(createRouteNodeSelector(''))(Root)
```

Using `createRouteNodeSelector` with other connect properties:

```javascript
export default connect(state => {
    const routeNodeSelector = createRouteNodeSelector('');

    return (state) => ({
        a: state.a,
        b: state.b,
        ...routeNodeSelector(state)
    })
)(Root);
```

## With immutable-js

If you are using [immutable-js](https://github.com/facebook/immutable-js) and [redux-immutable](https://github.com/gajus/redux-immutable) simply use the reducer from 'redux-router5/immutable/reducer'

```javascript
import router5Reducer from 'redux-router5/immutable/reducer'
```


# Advanced


# Plugins

router5 is extensible with the use of plugins. Plugins can decorate a route instance and do things on specific router and transition events.

## Plugin requirements

A plugin is a function taking a router instance and returning an object with a name and at least one of the following methods:

* `onStart()`: invoked when `router.start()` is called
* `onStop()`: invoked when `router.stop()` is called
* `onTransitionStart(toState, fromState)`
* `onTransitionCancel(toState, fromState)`
* `onTransitionError(toState, fromState, err)`
* `onTransitionSuccess(toState, fromState, opts)` (options contains `replace` and `reload` boolean flags)
* `teardown()`: a function called when removing the plugin

## Registering a plugin

```javascript
function myPlugin(router, dependencies) {
    return {
        onTransitionSuccess: (toState, fromState) => {
            console.log(
                'Yippee, navigation to ' + toState.name + ' was successful!'
            )
        }
    }
}

const router = createRouter()

router.usePlugin(myPlugin)
```

## Plugin examples

* [Browser plugin](https://github.com/router5/router5/blob/master/packages/router5-plugin-browser/modules/index.ts)
* [Persistent params plugin](https://github.com/router5/router5/blob/master/packages/router5-plugin-persistent-params/modules/index.ts)
* [Logger](https://github.com/router5/router5/blob/master/packages/router5-plugin-logger/modules/index.ts)

Router5 includes a logging plugin that you can use to help development

```javascript
import createRouter, { loggerPlugin } from 'router5'

const router = createRouter()

const teardownPlgin = router.usePlugin(loggerPlugin)
```


# Middleware

Multiple middleware functions can be registered with a router instance. They are invoked in series after the router has made sure active route segments can be deactivated and future active route segments can be activated. Middleware functions are for example a great way to load data for your routes.

## Registering middleware functions

A middleware is a function taking a router instance and registered dependencies (like lifecycle methods and plugins) and returning a function which will be called on each transition (unless a transition failed at the *canActivate* or *canDeactivate* state).

A middleware function can return a boolean for synchronous results, a promise or call a done callback for asynchronous operations. If it returns false, a rejected promise or a callback with an error, it will fail the transition.

This type of function is ideal to remove data loading logic from components, and is a good fit for applications aiming at having a centralised state object.

```javascript
const mware1 = (router) => (toState, fromState, done) => {
    // Let's fetch data and call done
    done();
};

const mware2 = (router) => (toState, fromState, done) => {
    // Let's fetch data and call done
    done();
};

router.useMiddleware(mware1, mware2);
```

`useMiddleware` can be called multiple times, but keep in mind that registration order matters. You can clear all your middleware functions by using `router.clearMiddleware()`.

## Adding data to state

It is possible to mutate the `toState` object by adding properties, or to pass a new state object in callbacks or promises. When passing a new object, the router will ignore it if initial state properties (`name`, `params` and `path`) are changed.

```javascript
import { getData } from './dataApi';

const dataLoader = router =>
    (toState, fromState) =>
        // toState object will be extended with data values
        getData().then(data => ({ ...toState, ...data }));
```

## Custom errors

When failing a transition in a middleware function, custom errors can be returned. Custom errors can be a string or an object:

* when a string, the router will return `{ code: 'TRANSITION_ERR', error: '<your string>'}`
* when an object, the returned error object will be extended with your error object `{ code: 'TRANSITION_ERR', ...errorObject }`


# Preventing navigation

It is a common case to want to allow / prevent navigation away from a view or component: if a User is in the middle of completing a form and data has not been saved, you might want to warn them about data being lost or prevent them to leave the current view until data has been saved.

## Using lifecycle functions

Router5 supports `canActivate` and `canDeactivate` functions for route segments:

* `canActivate` functions are called on segments which will become active as a result of a route change
* `canDeactivate` functions are called on segments which will become inactive as a result of a route change

Both functions have the same signature than middleware functions. Their result can be synchronous (returning a boolean) or asynchronous (returning a promise or calling `done(err, result)`).

{% hint style="info" %}
if a canActivate or canDeactivate function doesn't return a boolean, a promise or doesn't call back, the transition will not proceed.
{% endhint %}

```javascript
const canActivate = (router) => (toState, fromState) => {
    return true;
}

router.canActivate('admin', canActivate);
```

`canActivate` functions are called from top to bottom on newly activated segments. `canDeactivate` methods are invoked from bottom to top.

### Using middleware functions

Middleware functions behave like `canActivate` and `canDeactivate`. Read more about [middleware](/advanced/middleware).


# Errors and redirections

When failing a transition function (canActivate, canDeactivate, middleware) custom errors can be returned. Custom errors can be a string or an object and will be added to the error object and passed to `start` and `navigate` callbacks).

## Custom errors

Custom errors can be a string (error code) or an object. They can be passed using the first argument of `done` callbacks or encapsulated in failed promises.

### A string

```javascript
router.canActivate('profile', (router) => (toState, fromState, done) => {
    done('my custom error');
});

router.navigate('profile', (err, state) => {
    /* Error:
    {
        code: 'CANNOT_ACTIVATE',
        segment: 'profile',
        error: 'my custom error'
    }
    /*
})
```

### An object

```javascript
router.canActivate('profile', (router) => (toState, fromState, done) => {
    done({
        why: 'because'
    });
});

router.navigate('profile', (err, state) => {
    /* Error:
    {
        code: 'CANNOT_ACTIVATE',
        segment: 'profile',
        why: 'because'
    }
    */
})
```

## Redirecting after an error

When you fail a transition, you can pass a `redirect` property to specify what the router should do next. `redirect` must be an object containing the route name you want to redirect to (`name`) and optionally can contain params (`params`).

```javascript
router.canActivate('profile', (router) => (toState, fromState, done) => {
    return isUserLoggedIn()
        .catch(() => Promise.reject({ redirect: { name: 'login' }}));
});

router.navigate('profile', (err, state) => {
    // err is null
    state.name === 'login';
});
```


# Dependency injection

When using lifecycle methods (`canActivate`, `canDeactivate`), middleware or plugins, you might need to access specific objects from your application: a store, a specific API, etc... You can pass their reference to router5 and they will be passed alongside your router instance.

For TypeScript users, `createRouter` accepts a generic for typing dependencies.

You can register all dependencies at once, or one by one.

```javascript
const router = createRouter(routes, options, dependencies)
```

```javascript
router.setDependencies({ store, api })
// or
router.setDependency('store', store)
router.setDependency('api', api)
```

You can retrieve your current dependencies references using `getDependencies()`.

Lifecycle methods (`canActivate`, `canDeactivate`), middleware or plugins will be called with them:

```javascript
const plugin = (router, dependencies) => ({
    /*
        onStart() {},
        onStop() {},
        onTransitionStart() {},
        ...
    */
})
```

```javascript
const canActivate = (router, dependencies) =>
    (toState, fromState, done) {
        /* ... */
    }
```

```javascript
const middleware = (router, dependencies) =>
    (toState, fromState, done) {
        /* ... */
    }
```


# Loading async data

Loading async data is always an important task of a web application. Very often, data and routes are tied to your application business logic. Therefore, loading data on a route change is very common.

The way data loading can work with routing depends on what you might call your "routing strategy":

* Do you want a route transition to wait for data to be loaded?
* Do you want a route transition to fail if data cannot be loaded?
* How do you bind your view to data?

There are many ways to handle data coming from a router and from an API:

* your components can receive them both at the same time
* your components can receive a route update first and then a data update later
* your components can receive a route update first and decide to load data
* etc...

Router5 doesn't provide an opinionated way of handling async data, instead this article demonstrates the tools router5 can provide to help you loading data. You shouldn't view those examples as *the* way to load data, their purpose is purely illustrative and they don't cover every case (error handling, server-side data loading, etc...). Instead you should aim to do things and organise your code the way you think is best for you and your application.

## Using a middleware

> You can use your router state objects as a container for route-specific data.

You can use a middleware if you want your router to wait for data updates and/or prevent a route transition to happen if data loading fails. When doing so, you can use `toState` state object as a container for your route-specific data: your router will emit the mutated state.

First, we need to define what data need to be loaded for which route segment:

```javascript
const routes = [
    {
        name: 'home',
        path: '/home'
    },
    {
        name: 'users',
        path: '/users',
        onActivate: (params) => fetch('/users').then(data => ({ users: data.users }))
    },
    {
        name: 'users.user',
        path: '/:id',
        onActivate: (params) => fetch(`/users/${params.id}`).then(data => ({ user: data.user }))
    }
]
```

Then we create a middleware function which will invoke data for the activated segments on a route change. In this example, data are loaded in parallel using `Promise.all`. You can proceed differently by loading data in series, or by implementing dependencies between your `onActivate` handlers.

```javascript
import transitionPath from 'router5-transition-path';

const dataMiddlewareFactory = (routes) => (router) => (toState, fromState) => {
    const { toActivate } = transitionPath(toState, fromState);
    const onActivateHandlers =
        toActivate
            .map(segment => routes.find(r => r.name === segment).onActivate)
            .filter(Boolean)

    return Promise
        .all(onActivateHandlers.map(callback => callback()))
        .then(data => {
            const routeData = data.reduce((accData, rData) => Object.assign(accData, rData), {});
            return { ...toState, data: routeData };
        });
};
```

And when configuring your router:

```javascript
import { routes } from './routes';

const router = createRouter(routes);
/* ... configure your router */

/* data middleware */
router.useMiddleware(dataMiddlewareFactory(routes));
```

In the case you don't want a route transition to wait for data to be loaded, you cannot use the router state object as a data container. Instead, you should load data from your components or use a state container like [redux](https://redux.js.org).

## Using a state container (redux)

> Using a state container like redux gives you a lot more flexibility with your routing strategy.

Because all data ends up in the same bucket that your components can listen to, data loading doesn't need to block route transitions. The only thing it needs is a reference to your store so actions can be dispatched. As a result, your view can represent with greater details the state of your application: for example your UI can be a lot more explicit about displaying loading feedback. Not blocking route transitions also means immediate URL updates (history), making your app feel more responsive.

The following example assumes the use a redux store configured with a `redux-thunk` middleware.

```javascript
import { loadUsers, loadUser } from './actionCreators';

const routes = [
    {
        name: 'home',
        path: '/home'
    },
    {
        name: 'users',
        path: '/users',
        onActivate: (params) => (dispatch) =>
            fetch('/users').then(data => dispatch(loadUsers(data.users)))
    },
    {
        name: 'users.user',
        path: '/:id',
        onActivate: (params) => (dispatch) =>
            fetch(`/users/${params.id}`).then(data => dispatch(loadUser(data.user)))
    }
]
```

You need to create your store and router, and pass your store to your router instance (with `.setDependency()`):

```javascript
router.setDependency('store', store);
```

Then we create a router5 middleware for data which will load data on a transition success.

```javascript
import { actionTypes } from 'redux-router5';
import transitionPath from 'router5-transition-path';

const onRouteActivateMiddleware = (routes) => (router, dependencies) => (toState, fromState, done) => {
    const { toActivate } = transitionPath(toState, fromState);

    toActivate.forEach(segment => {
        const routeSegment = routes.find(r => r.name === segment);

        if (routeSegment && routeSegment.onActivate) {
            dependencies.store.dispatch(routeSegment.onActivate(toState.params));
        }
    });

    done();
};
```

Finally, just create your store and include `onRouteActivateMiddleware(routes)` middleware.

## Async data loading and universal applications

The two examples above show two different techniques of loading data with a router5 middleware. One is blocking, one is non-blocking. But what about universal applications?

The answer is very simple: block on the server-side, and choose to block or not on the client-side! For the example with example, you would need dispatch to return promises (with redux-thunk, your thunks need to return promises for their async operations).

## Chunk loading

Chunk loading (loading code asynchronously) is similar to data loading, since one can consider code is a form of data. With middlewares, you can call a done callback or return a promise, making them perfectly usable with `require.ensure` or `System.import`. Like examples above, you can implement similar techniques with, let's say, a `loadComponent` route property.

```javascript
const routes = {
        name: 'users',
        path: '/users',
        onActivate: (params) => (dispatch) =>
            fetch('/users').then(data => dispatch(loadUsers(data.users))),
        loadComponent: () => import('./views/UsersList')
    },
};
```

Then what you need is a middleware triggering `loadComponent`.

There are also emerging techniques of anticipated loading rather than lazy loading (i.e. from a specific view / component, chunks are loaded in anticipation of where a user is likely to go next). We could implement a `relatedComponents` property.

```javascript
const routes = [
    {
        name: 'home',
        path: '/home',
        loadComponent: () => import('./views/Home'),
        relatedComponents: [ 'users' ]
    },
    {
        name: 'users',
        path: '/users',
        onActivate: (params) => (dispatch) =>
            fetch('/users').then(data => dispatch(loadUsers(data.users))),
        loadComponent: () => import('./views/UsersList'),
        relatedComponents: [ 'home' ]
    },
};
```

Then on a transition, what you might want to consider this strategy:

* Load data and component (chunk)
* Once done, request idle callback to start loading sibling components


# Universal routing

*Router5* is capable to run on the server and in clients. This enables you to reuse the same routes for both client-side navigation and server-side pre-rendering. This is essentially done via two steps:

1. **Server-side** - Pass to your router the current URL (using `start`), and pass the resolved state to your client.
2. **Client-side** - Pass to your router the starting state received from the server and pass it to your router, so it can start with the provided state (and won't run the transition to the already activated page).

## Create your router (client & server)

You can use the same code for configuring your router on both client and server sides. The history plugin, for example, can be safely used on Node.js and in browsers.

```javascript
const createRouter = require( 'router5' ).default;
const browserPlugin = require( 'router5-plugin-browser' );

function createRouter() {
    const router = createRouter([
            { name: 'home', path: '/home' },
            { name: 'about', path: '/about' },
            { name: 'contact', path: '/contact' },
            { name: '404', path: '/404' }
        ], {
            trailingSlash: true,
            defaultRoute: '404'
        })

    router.usePlugin(browserPlugin({
            useHash: false
        }))

    return router
}

export default createRouter
```

## Server-side Routing

> This example is an [Express](http://expressjs.com/) with [Swig](http://paularmstrong.github.io/swig/) application. Make changes where needed to suit your preferred frameworks.

For universal applications, you need to:

* Create a new router instance for each request, using the request URL
* Send the state to the client and start your router with this initial state

`server.js`

```javascript
import express from 'express';
import createRouter from 'router5';
import swig from 'swig';

const app = express();

// Swig is used for templating in this example
// Use what you are comfortable with
app.engine( 'html', swig.renderFile );
app.set( 'view engine', 'html' );
app.set( 'views', './views' );

app.get( '*', ( req, res ) => {
    // Create a new router for each request
    const router = createRouter();

    router.start( req.originalUrl, function done( error, state ) {
        if ( error ) {
            res.status( 500 ).send( error );
        } else {
            res.send(/* Use your router state, send some HTML! */ );
        }
    });

} );

app.listen( 8080, function logServerStart() {
    console.log( 'Server is listening on port 8080...' );
} );
```

`base.html`

```markup
<!doctype html>
<html lang="en-US">
    <head>
        <title>Example Server-side Routing</title>
    </head>

    <body>
        <script src="/js/router.js"></script>
        <script type="text/javascript">
            /**
             * Load the App's inital state from the server
             * @type {JSON}
             */
            var initialState = JSON.parse('{{ initialState | safe }}');


            /**
             * Start our Router
             * @param  {Error} error  Router start error
             * @param  {Object} state State Object
             * @return {undefined}
             */
            app.router.start(initialState, function(error, state) {
                if (error) console.error('router error', error);
            });
        </script>
    </body>

</html>
```

From here forth, you can continue to use router5 as if it was a regular Single-Page Application.

## Performance

A new router has to be created server-side on each request. If your app is large (containing dozens of routes), the creation of your router will take up to a couple of hundred milliseconds.

Instead of creating a new router for each request, router5 includes a cloning mechanism: create a base router, and clone it for each request.

{% hint style="info" %}
A user reported a gain from 300ms to 10ms per request for creating a new router, with cloning.
{% endhint %}

```javascript
import { createRouter, cloneRouter } from 'router5'

const baseRouter = createRouter(/* ... */);

const router = cloneRouter(baseRouter);
```


# Listeners plugin

{% hint style="info" %}
`router.subscribe` is now available and as a result listeners plugin is no longer needed by `react-router5`. This will be deprecated in a near future.
{% endhint %}

## Usage

```javascript
import listenersPlugin from 'router5-plugin-listeners'

const router = createRouter()

router.usePlugin(listenersPlugin())
```

## Types of listeners

Listeners are called with `toState` and `fromState` arguments.

### Listen to a node change

`addNodeListener(name, fn)` will register a listener which will be invoked when the specified route node is the **transition node** of a route change, i.e. the intersection between deactivated and activated segments.

## Listen to any route change

Listeners registered with `addListener(fn)` will be triggered on any route change, including route reloads (*toState* will be equal to *fromState*). You can remove a previously added listener by using `removeListener(fn)`.

## Listen to a specific route

`addRouteListener(name, fn)` will register a listener which will be triggered when the router is navigating to the supplied route name.


# API Reference

## Core API

### createRouter

Create a router instance

```javascript
const router = createRouter([routes], [options], [dependencies])
```

* `routes`: your application routes, see [defining routes](/guides/defining-routes)
* `options`: your router options, see [router options](https://github.com/router5/router5/tree/1cc1c6969a96918deb28e45b8c5b2d6aa19d0a19/docs/guides/router5-options.md)
* `dependencies`: the dependencies you want to make available in middleware and plugins, see [dependency injection](https://github.com/router5/router5/tree/1cc1c6969a96918deb28e45b8c5b2d6aa19d0a19/docs/adavanced/dependency-injection.md)

### cloneRouter

Clone an existing router.

```javascript
const clonedRouter = cloneRouter(router, dependencies)
```

* `router`: the router instance to clone
* `dependencies`: the new dependencies, for the cloned router (optional)

### add

Add routes, see [defining routes](/guides/defining-routes)

```javascript
router.add(routes)
```

### start

```javascript
router.start(startPathOrState, [done])
```

* `startPathOrState`: a starting path (string) or state (object). When using `browserPlugin`, this argument is optional: path will be read from the document location
* `done`: a done callback (`done(err, state)`)

### isStarted

Check if the router is in a started state

```javascript
router.isStarted()
```

### navigate

Navigate to a new route

```javascript
router.navigate(routeName, [routeParams], [options], [done])
```

* `routeName`: the name of the route to navigate to
* `routeParams`: the route params
* `options`: options for the transition (`replace`, `reload`, `skipTransition`, `force` or any custom option)
* `done`: a done callback (`done(err, state)`)

### navigateToDefault

Navigate to the default route (if any)

```javascript
router.navigateToDefault([opts], [done])
```

### Stop

Stop your router

```javascript
router.stop()
```

### cancel

Cancel the current transition (if any)

```javascript
router.cancel()
```

### forward

Set a route to forward to another route (when navigating to the first one)

```
router.forward(fromRoute, toRoute)
```

### getState

Return the current state

```javascript
const state = router.getState()
```

### getOptions

Return current options

```javascript
router.getOptions()
```

### setOption

Set an option

```javascript
router.setOption(name, value)
```

### setDependency

Set a dependency

```javascript
router.setDependency(dependencyName, dependency)
```

### setDependencies

Set dependencies

```javascript
router.setDependencies(dependencies)
```

### getDependencies

Return the current dependencies

```javascript
const dependencies = router.getDependencies()
```

### useMiddleware

Register one or multiple middlewares, see [middleware](/advanced/middleware)

```javascript
const remove = router.useMiddleware(...middlewares)
```

### clearMiddleware

Remove all middleware

```javascript
router.clearMiddleware()
```

### usePlugin

Register one or multiple plugins, see [plugins](/advanced/plugins)

```javascript
const teardown = router.usePlugin(...plugins)
```

### canActivate

Set a `canActivate` handler for the provided route name, see [preventing navigation](/advanced/preventing-navigation)

```javascript
router.canActivate(name, canActivateHandler)
```

### canDeactivate

Set a `canDeactivate` handler for the provided route name, see [preventing navigation](/advanced/preventing-navigation)

```javascript
router.canDeactivate(name, canDeactivateHandler)
```

### clearCanDeactivate

Remove a `canDeactivate` handler for the provided route name

```javascript
router.clearCanDeactivate(name)
```

### buildPath

Build a path given a route name and params

```javascript
router.buildPath(route, params)
```

### matchPath

Attempt to match a path

```javascript
router.matchPath(path, [source])
```

### setRootPath

Set the root path

```javascript
router.setRootPath(rootPath)
```

### isActive

Check if the provided route is currently active

```javascript
router.isActive(name, params, [strictEquality], [ignoreQueryParams])
```

* `name`: the route name
* `params`: the route params
* `strictEquality`: whether to check if the given route is the active route, or a descendant of the active route (`false` by default)
* `ignoreQueryParams`: whether to ignore query params (`true` by default)

### areStateEqual

Compare two route state objects

```javascript
router.areStatesEqual(state1, state2, ignoreQueryParams)
```

### areStatesDescendants

Check if a state is a descendant of another state

```javascript
router.areStatesDescendants(parentState, childState)
```

## Browser plugin

See [in the browser](/guides/in-the-browser)

The browser plugin adds the following to your router instance:

### buildUrl

Build an URL

```javascript
router.buildUrl(routeName, routeParams)
```

### matchUrl

Match an URL

```javascript
router.matchUrl(url)
```

### replaceHistoryState

Replace state in history and silently update your router instance state. Use if you know what you are doing.

```javascript
router.replaceHistoryState(name, params)
```


# Migration


# Migrating from 7.x to 8.x

router5 is three and a half years old, and a few things have changed since the start! This year (2018), we went from 2,000 downloads a week to 10,000 downloads a week, so thank you! It gave me the motivation to perform a complete refactor with a necessary modernisation of all packages, something I had procrastinated over for a while. I'm always happy to know more about users of router5, so come and say I: [who is using router5?](https://github.com/router5/router5/issues/161).

With version 7, all packages have been rewritten in TypeScript, and tooling is now consistent across packages. A few breaking changes have been introduced.

## No longer maintained packages

An `unmaintained` directory has been created to move packages which are no longer maintained. `deku-router5` has been added to it: if you are interested in maintaining it, I'm happy to consider transferring ownership.

## Changes per package

### router5

#### Features

* Plugins now accept a `teardown` method (alongside `onStart`, `onStop`, `onTransitionSuccess`, etc.): it will be called when a plugin is removed.

#### BREAKING CHANGES

* Plugins previously included with the router5 package (browser plugin, logger plugin, listeners plugin and persistent params plugin) have been moved to their own packages:
  * `router5-plugin-browser`
  * `router5-plugin-logger`
  * `router5-plugin-listeners`
  * `router5-plugin-persistent-params`
* `useMiddleware` no longer returns your router instance, but a function to remove the added middleware. You can still pass multiple middleware, in which case calling the teardown function will remove all of them.
* `usePlugin` no longer returns your router instance, but a function to remove the added plugin. You can still pass multiple plugins, in which case calling the teardown function will remove all of them.
* `hasPlugin` method has been removed, and `pluginName` is no longer needed
* Cloning is now done using a `cloneRouter` function, and it no longer re-uses existing dependencies

  ```javascript
    import { cloneRouter } from 'router5'

    const clonedRouter = cloneRouter(router, dependencies)
  ```
* When `reload` navigate option is set to `true`, `fromState` is no longer set to `null`: `transitionPath` has been updated to take into account this change, if you have middleware or plugins with custom logic, make sure you update them.

### react-router5

#### Features

* Hooks have been added: `useRoute`, `useRouteNode` and `useRouter`

#### BREAKING CHANGES

* `Link` has been renamed to `ConnectedLink`, and `BaseLink` has been renamed to `Link`
* `RouteProvider` has been renamed to `RouterProvider`: there is now only one provider
* `react-router5` now requires React version 16.3.0 or above: it no longer uses the old context API. The migration path is quite easy:
  * If you are not using React 16.3.0 (or above), and cannot upgrade to it, a new package `react-router5-hocs` has been added: it is a drop-in replacement for `react-router5` (Link component names stil need changed, see point above)
  * If you are using React 16.3.0 and above, you are good to continue using `react-router5` (see breaking changes above)

### redux-router5

#### BREAKING CHANGES

* For use with immutable.js, import `router5Reducer` from `redux-router5-immutable`

### router5-helpers

#### BREAKING CHANGES

* Undocumented `redirect` helper has been removed: it's no longer needed with `forwardTo`.


# Migrating from 6.x to 7.x

router5 is three and a half years old, and a few things have changed since the start! This year (2018), we went from 2,000 downloads a week to 10,000 downloads a week, so thank you! It gave me the motivation to perform a complete refactor with a necessary modernisation of all packages, something I had procrastinated over for a while. I'm always happy to know more about users of router5, so come and say I: [who is using router5?](https://github.com/router5/router5/issues/161).

With version 7, all packages have been rewritten in TypeScript, and tooling is now consistent across packages. A few breaking changes have been introduced.

## No longer maintained packages

An `unmaintained` directory has been created to move packages which are no longer maintained. `deku-router5` has been added to it: if you are interested in maintaining it, I'm happy to consider transferring ownership.

## Changes per package

### router5

#### Features

* Plugins now accept a `teardown` method (alongside `onStart`, `onStop`, `onTransitionSuccess`, etc.): it will be called when a plugin is removed.

#### BREAKING CHANGES

* Plugins previously included with the router5 package (browser plugin, logger plugin, listeners plugin and persistent params plugin) have been moved to their own packages:
  * `router5-plugin-browser`
  * `router5-plugin-logger`
  * `router5-plugin-listeners`
  * `router5-plugin-persistent-params`
* `useMiddleware` no longer returns your router instance, but a function to remove the added middleware. You can still pass multiple middleware, in which case calling the teardown function will remove all of them.
* `usePlugin` no longer returns your router instance, but a function to remove the added plugin. You can still pass multiple plugins, in which case calling the teardown function will remove all of them.
* `hasPlugin` method has been removed, and `pluginName` is no longer needed
* Cloning is now done using a `cloneRouter` function, and it no longer re-uses existing dependencies

  ```javascript
    import { cloneRouter } from 'router5'

    const clonedRouter = cloneRouter(router, dependencies)
  ```
* When `reload` navigate option is set to `true`, `fromState` is no longer set to `null`: `transitionPath` has been updated to take into account this change, if you have middleware or plugins with custom logic, make sure you update them.

### react-router5

#### Features

* Hooks have been added: `useRoute`, `useRouteNode` and `useRouter`

#### BREAKING CHANGES

* `Link` has been renamed to `ConnectedLink`, and `BaseLink` has been renamed to `Link`
* `RouteProvider` has been renamed to `RouterProvider`: there is now only one provider
* `react-router5` now requires React version 16.3.0 or above: it no longer uses the old context API. The migration path is quite easy:
  * If you are not using React 16.3.0 (or above), and cannot upgrade to it, a new package `react-router5-hocs` has been added: it is a drop-in replacement for `react-router5` (Link component names stil need changed, see point above)
  * If you are using React 16.3.0 and above, you are good to continue using `react-router5` (see breaking changes above)

### redux-router5

#### BREAKING CHANGES

* For use with immutable.js, import `router5Reducer` from `redux-router5-immutable`

### router5-helpers

#### BREAKING CHANGES

* Undocumented `redirect` helper has been removed: it's no longer needed with `forwardTo`.


# Migrating from 5.x to 6.x

With version 6.0.0, router options have been reworked to be more intuitive and more flexible to use. Defaults have changed, but you can set options so your current URLs remain unchanged.

## Feature

* `router5`
  * Navigation options are now added to state objects (in `meta`)
  * You can now specify your custom navigation options: they will be added to state objects and are usable by your custom plugins and middlewares
  * New `queryParams` option to configure how query parameters are built, and how they are parsed
  * New `caseSensitive` option (default to `false`)
* `react-router5`
  * Alternative components using a render function have been added in addition to the higher-order components. Those components require a new provider, because they leverage React new context API (React >= 16.3, see <https://github.com/router5/router5/tree/master/packages/react-router5>). Higher-order components won't be deprecated, and will evolve to use React new context API once deprecated.

## Bug fix

* `router5`
  * Navigation with browser plugin and `useHash` on IE11 fixed when manually changing the URL
  * Transition phase was reowrked to support correctly state mutations in middlewares using the done callback or promises

## Breaking changes

* Path matching used to be case sensitive and it is now case insensitive by default (new `caseSensitive` option)
* Query parameters in paths can no longer be defined with `[]` (brackets should be removed)
* Option `trailingSlash` has been renamed to `strictTrailingSlash`: by default it is `false`
* Option `useTrailingSlash` has been renamed to `trailingSlashMode` with value being `'default'`, `'never'` or `'always'`
* Option `strictQueryParams` has been renamed to `queryParamsMode` with value being `'default'`, `'strict'` or `'loose'`
* Query parameters: by default boolean values are now stringified to `'true'` and `'false'`, null values are stringified without `=` sign (`{ param: null }` will be stringified to `'?param'`). To keep your current behaviour intact, set `queryParams.nullFormat` to `'hidden'` and `queryParams.booleanFormat` to `'empty-true'` (see options below)
* Private method router.makeState signature has changed (you shouldn't be impacted)

**As described above, options have been revamped to make it easier and more intuitive to configure:**

* trailingSlashMode:
  * `'default'`: building follows path definitions
  * `'none'`: when building, trailing slash is removed
  * `'always'`: when building, trailing slash is added
* queryParamsMode:
  * `'default'`: a path will match with any query parameters added, but when building, extra parameters won't appear in the returned path.
  * `'strict'`: a path with query parameters which were not listed in node definition will cause a match to be unsuccessful. When building, extra parameters won't appear in the returned path.
  * `'loose'`: a path will match with any query parameters added, and when building, extra parameters will appear in the returned path.
* queryParams:
  * `arrayFormat`: Specifies how arrays should be stringified
    * `'none'` (default): no brackets or indexes are added to query parameter names (`'role=member&role=admin'`)
    * `'brackets`: brackets are added to query parameter names (`'role[]=member&role[]=admin'`)
    * `'index'`: brackets and indexes are added to query parameter names (`'role[0]=member&role[1]=admin'`)
  * `booleanFormat`: specifies how boolean values are stringified and parsed
    * `'none'` (default): booleans are stringified to strings (`'istrue=true&isfalse=false'`)
    * `'empty-true'`: same as `'none'` except true values are stringified without value (`'istrue&isfalse=false'`). If you choose this boolean format, make sure to change the value of `'nullFormat'`.
    * `'string'`: same as `'none'` but `'true'` and `'false'` are parsed as booleans
    * `'unicode'`: `true` and `false` are displayed with unicode characters, and parsed as booleans (`'istrue=✓&isfalse=✗'`)
  * `nullFormat`: specifies how null values are stringified and parsed
    * `'default'` (default): null values are stringified without equal sign and value (`'isnull'`)
    * `'string'`: null values are stringified to `'null'` (`'isnull=null'`) and parsed as null values
    * `'hidden'`: null values are not stringified


# Migrating from 4.x to 5.x

With version 5.0.0, `router5` is now a monorepo: all repos have been imported into <https://github.com/router5/router5>. This will make maintenance and release of new versions easier.

## Bug fixes

Not found state is now passed to middleware functions on start: if you use `allowNotFound` option and have custom middleware functions, make sure they still work.

## Default options

* router `strictQueryParams` option is now `false` by default: if you currently don't specify that option, you need to explicitely set it to `true` to keep the same behaviour.
* browser plugin option `preserveHash` is now `true` by default

## Packages renamed

* `router5.helpers` package has been renamed to `router5-helpers`
* `router5.transition-path` package has been renamed to `router5-transition-path`


# Migrating from 3.x to 4.x

With version 4.0.0, *router5* has been refactored. API for plugins, middleware functions, `canActivate` and `canDeactivate` functions are now consistent.

## Router instanciation

*router5* default export is now a `createRouter` function as opposed to a `Router5` class. The API is identical between `createRouter(routes, options)` and `new Router5(routes, options)`.

```javascript
import createRouter from 'router5';

const router = createRouter(routes, options);
```

## Plugins moved

`router5-history`, `router5-persistent-params` and `router5-listeners` have been moved to router5 main repository. They are no longer individual modules but are distributed with router5 module.

```javascript
import browserPlugin from 'router5-plugin-browser';
import listenersPlugin from 'router5-plugin-listeners';
import persistentParamsPlugin from 'router5-plugin-persistent-params';
```

The history plugin has been renamed 'browser plugin', to better describe its responsabilities. It deals with any URL related options and methods, to make router5 fully runtime environment agnostic:

* `useHash`, `hashPrefix` and `base` options need to be passed to `browserPlugin`, not router5
* `buildUrl`, `matchUrl` and `urlToPath` methods are no longer present by default and are added to your router instance by `browserPlugin`.

```javascript
import browserPlugin from 'router5-plugin-browser';

router.usePlugin(browserPlugin({
    useHash: true
}));
```

## Dependency injection reworked

Dependency injection has been reworked: `setAdditionalArgs` has been renamed to `setDependencies` / `setDependency` and `getAdditionalArgs` has been renamed to `getDependencies`.

```javascript
router.setDependency('store', store);
// Or
router.setDependencies({ store });

router.getDependencies(); // => { store: store }
```

Dependencies are no longer injected before `toState` in middleware, canActivate and canDeactivate functions. Instead, they are injected alongside `router`, and are now available in plugins too. They are passed as an object of key / value pairs: it will now be easier to share code, plugins, middleware without sharing the exact same dependencies.

## Middleware and route lifecycle functions alignment

canActivate and canDeactivate functions have been reworked to be aligned with middleware functions.

```javascript
function isAdmin(router, dependencies) {
    return function (toState, fromState, done) {
        /* boolean, promise or call done */
    }
}

router.canActivate('admin', isAdmin);
```

Boolean shortcuts are still supported (`canActivate('admin', isAdmin)`).

More importantly, they are now **thunks**: they are executed when added, and their returned functions will be executed when required by the router. It enables the use of **closures**.

## Unknown routes (not found) supported

A new `allowNotFound` option available, to give a new strategy to deal with unkown routes.

If `defaultRoute` option is not supplied and a path cannot be matched by the router, then the router will emit an `ROUTE_NOT_FOUND` error unless `allowNotFound` is set to true. In that case, the router will allow the transition to happen and will generate a state like the following one (given a User tried to navigate to an unknown URL `/route-not-found`):

```javascript
import { constants } from 'router5';

{
    name: constants.UNKNOWN_ROUTE
    params: { path: '/route-not-found' },
    path: '/route-not-found'
}
```

## Other notable changes

* AMD and globals bundle are no longer distributed, use the UMD bundle instead
* `usePlugin` and `useMiddleware` behave the same: you can supply one or more argument, and calling them thereafter will add more plugins / middleware functions (`useMiddleware` used to overwrite middleware functions)
* `errCodes` has been renamed to `errorCodes`
* Route parameters and transition options are now optional in `navigate`, allowing users to only supply a route name and a done callback (`router.navigate('home', () => { /* ... */ })`)
* A new `setRootNodePath` function has been added to configure the path of the root node. It can be used for example to list a number of allowed query parameters for all routes if `strictQueryParams` option is set to `true`.


# Migrating from 2.x to 3.x

## New features

* When a transition fails (either in a `canActivate`, `canDeactivate` or middleware function), a custom error can be returned containing a `redirect` property.
* Persistent parameters plugin now available.

## Breaking change

There are no breaking changes.

## Code example

Redirecting to a login page if the current user is not logged in:

```javascript
// With promises
router.canActivate(
    'profile',
    () => isLoggedIn()
        .catch(() => ({ redirect: { name: 'login' } }))
);

// With callbacks
router.canActivate(
    'profile',
    (toState, fromState, done) => {
        isLoggedIn()
            .then(() => done(null, toState))
            .catch(() => done(({ redirect: { name: 'login' } })))
    }
);
```


# Migrating from 1.x to 2.x

## New features

* You can now pass to `router.add()` objects containing `canActivate` functions and those functions will be registered. No need to call for each route `addNode` or `canActivate`.
* Persistent parameters plugin now available.

## Breaking change

* `router.registerComponent` and `router.deregisterComponent` have been removed in favour of `canDeactivate`
* Additional arguments now apply to middleware functions.
* Context has been removed from middleware functions.
* Middleware functions are now a function taking a router instance and returning a function called on each transition.
* Plugins, like middleware functions, are now a function taking a router instance and returning an object of methods (which doesn't contain an `init` function anymore).
* `autoCleanUp` is no longer shared with *router5-listeners*. If you need to turn off automatic deregistration of node listeners, pass `{ autoCleanUp: false }` to the listeners plugin.
* `router5` package now exports `Router5` as default, and `RouteNode`, `loggerPlugin`, `errCodes` and `transitionPath` as named exports

## Code example

**ES2015+**

```javascript
import Router5, { loggerPlugin } from 'router5';
import historyPlugin from 'router5-history';
import listenersPlugin from 'router5-listeners';

const router = new Router5()
    .add([{
        name: 'home',
        path: '/home'
    }])
    .usePlugin(historyPlugin())
    .usePlugin(listenersPlugin())
    // Development helper
    .usePlugin(loggerPlugin())
    .start();
```

**ES5**

```javascript
var router5 = require('router5');
var Router5 = router5.default;
var loggerPlugin = router5.loggerPlugin;

var historyPlugin = require('router5-history');
var listenersPlugin = require('router5-listeners');

var router = new Router5()
    .add([{
        name: 'home',
        path: '/home'
    }])
    .usePlugin(historyPlugin())
    .usePlugin(listenersPlugin())
    // Development helper
    .usePlugin(loggerPlugin())
    .start();
```


# Migrating from 0.x to 1.x

> *router5* is available in all major formats: **ES6**, **CommonJS**, and **UMD**.

It can be installed using **npm** or **yarn**. Alternatively, you can download a specific version from [github](https://github.com/router5/router5/releases).

## Installation

```bash
# yarn
yarn add router5
# npm
npm install router5
```

## Include *router5* in your application

**CommonJS**

```javascript
// ES2015+
import createRouter, { RouteNode, errorCodes, transitionPath, loggerPlugin, constants } from 'router5';

import browserPlugin from 'router5-plugin-browser';
import listenersPlugin from 'router5-plugin-listeners';
import persistentParamsPlugin from 'router5-plugin-persistent-params';

// ES5
var router5 = require('router5');

var createRouter = router5.default;
var RouteNode = router5.RouteNode;
var errorCodes = router5.errorCodes;
var constants = router5.constants;
var transitionPath = router5.transitionPath;
var loggerPlugin = router5.loggerPlugin;
var constants = router5.constants;

var browserPlugin = require('router5-plugin-browser');
var listenersPlugin = require('router5-plugin-listeners');
var persistentParamsPlugin = require('router5-plugin-persistent-params');
```

**UMD**

A UMD bundle is available in `/dist/umd`, and it should be used for AMD or globals. The bundle contains all *router5* dependencies (*route-node* and *path-parser*), but doesn't contain plugins.

Plugins are packaged separately and available in `/dist/umd`:

* `browserPlugin` UMD module is named `router5BrowserPlugin`
* `listenersPlugin` UMD module is named `router5ListenersPlugin`
* `persistentParamsPlugin` UMD module is named `router5PersistentParamsPlugin`

  bundle is named `router5ListenersPlugin`.


