<Router/>
Next-generation router for React tailored towards complex SPA use-cases, makes your application feel like it's been rendered on the server-side.
Last updated
Was this helpful?
Next-generation router for React tailored towards complex SPA use-cases, makes your application feel like it's been rendered on the server-side.
Last updated
Was this helpful?
Source code is hosted on
A demo can be found on
This project was heavily inspired by and a lot of attention has been paid to keep the API very similar, therefore the transition will be quick and hassle-free. This router implementation offers many convenient features that are usually too hard to implement in regular single-page applications. All of these features are on-demand, if you don't need them, don't use them. Out of the box, this router behaves exactly like the react-router. By adding some additional flags, your app starts to feel totally different. The UX increases drastically, your routes feel like they are being rendered on the server-side.
Declarative routing inside React
Nested routing
Async loading of components
Supports route loaders
Supports route unloaders
Routes feel like they've been rendered on the server
Controlled mode for premature rendering
Hooks for various use cases
Easy URL query manipulation
Switch, Redirect, Link, and other goodies
Uses for maximal compatibility with other libraries
This router emerged out of necessity to solve common SPA needs that are incredibly hard / almost impossible to solve without granular control over the router and its transitions. This is why this is not an addon on top of react-router, nor is it a fork. A totally new approach was needed to solve many problems in a developer-friendly way. To fully understand the reasons, let's have a look at the problems every single page application has to live with.
Imagine you take advantage of code splitting and lazy load your route implementation upon first navigation.
To achieve that, you have to wrap your route component into another component to display some sort of a loading overlay. As soon as the implementation has been loaded, you render the actual page.
For a brief moment, you will have to show a loading overlay to indicate the progress. Users will no longer see the previous screen and just stare and the loading overlay until the new route is ready to render. You can immediately tell that this is a SPA based on the bad UX.
Router solves this problem by fetching your route implementation behind the scenes, without unmounting the current route. You can show a loading bar on the top of the page in the meantime. Users will see the previous route plus the loading bar until the new route is ready for the transition. This feels more natural and is also what Google and Facebook do in their applications.
Imagine a route that renders a table with data, where data is fetched from the server through an API call.
To achieve that, you need to display a global loading overlay until the data has been loaded. You need to manually orchestrate the loading overlay if there are multiple components that you have to wait for. Another trick that many SPAs fall back to is the use of skeletons - you render some placeholders that indicate that there is more to come, but the view is not fully ready yet.
For a brief moment, you have no data to show and your users have either to stare at the loading overlay or at some placeholders without any meaningful content until the page is fully ready to render.
Router solves this problem by allowing every component to register a loader. It will wait for all loaders to fully resolve prior to doing the transition. You can show a progress bar at the top of the page, your users never have to stare on an empty screen or at placeholder components. Preloaders can be freely registered from any component that is being rendered inside the new route. You don't need to orchestrate anything manually. Adopting this pattern is a matter of minutes.
Route loaders can also return an unloader that is triggered when the route is being navigated away from.
Imagine your current route triggers an action that opens a modal. Inside that modal, you have a link to another route. By clicking that link, the modal disappears immediately as the transition begins.
Your components don't have the means to do anything prior to transitioning away. The transition starts immediately, the modal simply disappears and new content is shown. It would feel more natural if the modal would gracefully close, maybe with a nice animation, just before the new route is rendered.
Router solves this problem by allowing every component to register an unloader. It will wait for all unloaders to fully resolve prior to doing the transition. You can do whatever is necessary, like sending some data to the server, running animations, etc. Unloading of the current route is part of the route transition life cycle, therefore you can show a progress bar at the top of the page indicating users that the next route is still being loaded.
Imagine you try to load another route that needs to fetch some data. Wouldn't it be cool, if you could show a small overlay, covering the currently rendered screen, and providing some additional information to the users, something like "Preparing the rocket to launch", "Sending out the droids", etc.?
This can be achieved thanks to the controlled mode. This mode can be applied to a single route or to all routes at once. This mode allows a route to render content prior to it being fully ready to render / fully loaded.
Router achieves this behaviour by having two routes mounted at the same time, while contrary to the un-controlled mode, the route that is still being in transition will not be hidden, it will be visible side by side with the previous screen. Now it's up to you to decide what you want to show and when. Thankfully there are many convenient hooks that make dealing with the life cycle a piece of cake.
Let's have a look at what the typical router/route life cycle looks like:
Mount router
Mount routes
Register routes with router
Figure out active routes
Load route implementations if necessary
Mount new routes in hidden mode, render normally in controlled mode
Wait for route loaders to complete
Wait for route unloaders to complete
Unmount the previous routes
Show new routes
If any new routes are registered during the life cycle execution, the router will incorporate them in the transition. While performing transitions, users can be presented with a progress indicator, but they will never lose the currently rendered screen. You don't need to show any skeletons while fetching relevant data. Each route feels like it has been rendered on the server.
As you can see, this router implementation is incredibly powerful and flexible. You can have regular routes side by side with routes that preload content, unload gracefully, allow children components to delay transitions while async operations are being executed, render multiple routes side by side in the controlled mode, etc. You can freely mix and match different behaviors based on your requirements.
Basic router setup with some routes:
Paths can be matched in a regular or an exact mode. Path parameters can be matched in different ways using the additional ?
, +
and *
modifiers.
Match path in the exact mode:
Match path parameters that can be retrieved later:
Match zero or exactly one path segment:
Match one or multiple path segments:
Match zero or multiple path segments:
Due to the nature of how this router implementation works, it is considered a bad practice to mount and unmount routes conditionally:
Manually unmounting a route bypasses it's unloaders
All routes must be wrapped inside this component:
Define a base path for the router:
Enable loaders and unloaders for all routes:
Enable loaders and unloaders for a specific route:
Define how long the router waits for loaders and unloaders to register, default is 5ms
:
Enable detailed life cycle logs:
During the tests you might want to provide a history instance pointing to a specific location:
The Route component is used to connect your components to a specific location path:
Provide a custom render function:
Provide a component to render:
Load component implementation asynchronously:
Provide a render function with some async logic:
Specify a path for the route:
Route without a path will always match:
Match route path in the exact mode:
When nesting routes, you might want to provide an absolute route path instead of the relative one:
Routes can be freely nested inside each other, parent route's path is automatically used as the path prefix:
Navigate to another route by clicking a link.
Links that match the current path, have the property data-active
set to true
. You can change the styling of active links using this simple CSS rule:
By default, links respect the ctrl+click
, alt+click
and cmd+click
events as well as the target
property. Links clicked using a modifier key will use the default browser behavior instead of triggering the normal navigation. This can be disabled by setting the intercept
property to false
:
This component can be used to apply specific settings to all of its child routes. The disabled
flag is especially useful whenever you need to disable some routes without having to unmount them:
This component will trigger a redirect to another path upon render:
Retrieves router handle from the context, it exposes some useful methods and properties:
Retrieves a route handle from the context, it exposes some useful methods and properties:
Returns an object with all the routes that were detected by the router:
Route loaders can be used to preload data prior to triggering the route transition:
Make sure to also set the unloadable property on the route or the router if you want to return an unloader from the useRouteLoader hook.
You can also create a route loader without the callback and resolve it manually:
Check if this particular loader is running:
Route unloaders can be used to run some logic prior to the route is being unmounted:
You can also create a route unloader without the callback and resolve it manually:
Check if this particular unloader is running:
Get status of the current route:
Check if the router is loading anything anywhere on the page:
Check if the router is unloading anything anywhere on the page:
Check if the router is showing anything anywhere on the page:
Check if the route is loading:
Check if the route is unloading:
Check if the route is visible:
Returns current location object:
Matches current location against the given pattern:
Retrieve route params:
Retrieve with default parameters:
Retrieve a query handle that can be used to read and write data into the query part of the URL. By providing default values for each query part, you define a list of query fields that you want to control. You won't be able to read or modify query parts that are not part of this list. This allows different components to work with different parts of the query without having to worry about each other's reads and writes:
You can update a part of the query, and preserve the previous values, this operation will never modify query parts that are not part of the list with the default values:
You can override the whole query, query parts that were omitted will be stripped from the final query, this operation will never modify query parts that are not part of the list with the default values. Omitted query pieces will be replaced with the default value:
Retrieve a redirect function:
Redirect with query:
Redirect with hash:
Redirect and preserve query from the current URL:
Redirect and preserve hash from the current URL:
Create a path according to the base path that has been configured on the router:
You can specify a custom base path:
Retrieve a helper to turn any object into a URL query string:
Retrieve a helper to parse a URL query string:
Parameters can be accessed inside a route using the and hooks:
Conditionally mounting routes inside a component might lead to unexpected behaviour
Don't panic, we've got you covered! You can use the , or components whenever you plan to toggle available routes at runtime:
Pass a custom instance, this is useful for testing or when working with other libraries:
You can add a loader by using the hook:
You can add an unloader by using the hook.
You can narrow down what paths will be considered as matching, by setting the exact
property. Check the section for more details.
This component will render the first route that matches the current path, it accepts the same props as the component:
Check if the router is loading anything anywhere on the page, same as the hook:
Check if the router is unloading anything anywhere on the page, same as the hook:
Check if the router is showing anything anywhere on the page, same as the hook:
You can use the router instance to redirect to another path, same as the hook:
Check if the route is loading, same as the hook:
Check if the route is unloading, same as the hook:
Check if the route is visible same as the hook:
Route handle can be used to redirect to another path, same as hook:
Route parameters can be accessed directly through the route handle, same as the hook:
Route query can be accessed directly through the route handle, there is also the hook that can also be used to modify the query:
Route status can be accessed directly through the route handle, same as the hook:
Returns instance from the context: