react-named-router
Named router built on top of React Router. Allows you to control your urls more efficiently by abstracting them away from your application. Makes translation and parameterized url building easier. Fully compatible with react-router-config. It can also be used on server side rendering.
react-router-dom
components and is required as a peer dependency, therefore, you
can't use it (yet) on Native environment.
- Installation
- Usage
- Advanced usage
- Configuration first usage
- With react-router-config
- I18n (translated route paths)
- SSR
- TypeScript
- API
- Contribute
Installation
Using yarn:
$ yarn add react-named-router react-router-dom
Using npm:
$ npm install --save react-named-router react-router-dom
That's it ! You don't need anything more.
Usage
The basic setup example:
import React from 'react';
import { NamedRouter } from 'react-named-router';
const routes = [
{ name: 'home', path: '/' },
{ name: 'parent', path: '/parent', routes: [{ name: 'nested', path: '/parent/nested' }] },
{ name: 'parametrized', path: '/users/:userId' },
{ name: 'optional', path: '/tasks/:type?' },
];
const App = () => (
<NamedRouter routes={routes}>
{/* App content goes here */}
</NamedRouter>
);
export default App;
The routes configuration is relying on the same structure as react-router-config
and can be used for both naming and
route rendering via renderRoutes(route.routes)
(see documentation for more details).
Nesting routes is not mandatory and only helps on two cases:
- When you want to do certain things when a component has a certain route in its parents route.
- When you use
renderRoutes
fromreact-router-config
.
By default, NamedRouter
is using BrowserRouter
under the hood but you can change that through the routerComponent
prop.
NamedRoute
The basic component to render routing is the same as when you use React Router classic. The only difference is that it
will take a name
prop instead of path
.
import { NamedRoute, NamedSwitch } from 'react-named-router';
const MyComponent = () => (
<div className="wrapper">
<NamedSwitch>
<NamedRoute name="home" exact component={Home} />
<Route component={NotFound} />
</NamedSwitch>
</div>
);
The NamedRoute
component will throw an error if requested route does not exist. This will help ensuring that your app
uses only valid route names.
NamedLink
and NamedRedirect
To link and redirect throughout your application with named routing, you can use both the NamedLink
and NamedRedirect
which use the same API. They both take a to
prop that accept a valid route name (otherwise an error will be thrown).
<NamedLink to="home">Link to home</NamedLink>
You can pass parameters as key/value object to build your urls through the params
prop. If URL contains a required
parameter and not found in key/values pairs, an error will be thrown to ensure urls are always built the correct way.
// This is correct
const redirect = <NamedRedirect to="parametrized" params={{ userId: 42 }} />;
// This will throw
const link = <NamedLink to="parametrized">Link to home</NamedLink>;
Optional parameters will not throw an error if not given.
NamedSwitch
Due to the way React router's Switch
operates, it cannot work with NamedRoute
component. This is why react-named-router
also provides a NamedSwitch
component that does exactly the same, but requires a name
prop instead of path
on
its children.
As a reminder, the NamedSwitch
component only renders the first matching child component for current location. This
allows you to use routes that are not matching exact path.
Advanced usage
React Named Router also provides a context API that allows you to perform push
and replace
operations using route
naming. This API is available through an HOC or a
React Hook.
withNamedRouting(Component)
The withNamedRouting
HOC injects into your component a prop named namedRouting
which contains an object that allows
you to use the HTML history API methods push
and replace
with naming.
const MyComponent = withNamedRouting(({ namedRouting }) => (
<div>
I'm a message box
<button onClick={() => namedRouting.push('home')}>Go to home</button>
</div>
))
Both functions take a second argument params
to build parameterized URLs.
StaticRouter
and therefore
history API will not be available. The context will then only contain helper methods which are covered in the API
section. The only way to continue having those methods available is to provide NamedRouter
the history
prop
with a memory history object.
useNamedRouting()
This function is a React Hook and can only be used in React functional components. Prefer use this over the HOC because
it bloats less your React tree in the debugger and is much more friendly to test/mock. This hook returns exactly the
same object as the namedRouting
prop when using the HOC.
const MyComponent = () => {
const { push } = useNamedRouting();
return <button onClick={() => push('home')}>Go to home</button>;
}
Configuration first usage
react-named-router
allows you to leverage the power of your routes configuration while maintaining high flexibility
about where to render each route. The NamedRoute
component will use all fields from the matching route to build its
props:
// Configuration file
const routes = [
{ name: 'home', path: '/', component: Home },
{ name: 'userDetails', path: '/users/:userId', exact: true, component: UserDetails },
]
// Component file
const MyComponent = () => (
<>
<LeftContainer>
<NamedRoute name="home" />
</LeftContainer>
<RightContainer>
<NamedRoute name="userDetails" />
</RightContainer>
</>
)
react-router-config
With Since route configuration objects of react-named-router
are based on react-router-config
configuration objects, you
can simply use it as you would normally but adding an extra name
property to each route object having a path
property.
const Users = ({ route }) => renderRoutes(route.routes);
const routes = [
{ name: 'home', path: '/', exact: true, component: Home },
{
name: 'users',
path: '/users',
component: Users,
routes: [
{ name: 'userDetails', path: '/users/:userId', component: UserDetails, exact: true },
{ component: UsersList },
],
},
];
const App = () => renderRoutes(routes);
I18n (translated route paths)
As said earlier i18n is really easy using named routing. Here's an example using react-i18next
:
i18next.addResourceBundle('en', 'urls', { users: 'users' });
i18next.addResourceBundle('fr', 'urls', { users: 'utilisateurs' });
const App = ({ lang }) => {
const { t } = useTranslation();
const routes = useMemo(() => [
{ name: 'users', path: t('urls:users') }
], [lang]);
return (
<NamedRouter routes={routes}>
{/* App content */}
</NamedRouter>
)
}
You can also base lang on a lang param inside URL.
SSR
Server side rendering is a cool feature that ensure great performance for mobile users and better SEO, and named
router can also help with that. You need to use the StaticRouter
instead of the regular BrowserRouter
by
providing the routerComponent
prop:
const handleGet = (req, res) => {
res.send(ReactDOMServer.renderToString((
<NamedRouter routerComponent={StaticRouter} routerProps={{ location: req.url }} routes={routes}>
{/* App content */}
</NamedRouter>
)));
}
You can also use the buildRoutingContext
to determine if you should return 404 not found:
const routingContext = buildRoutingContext(routes);
const handleGet = (req, res) => {
const route = routingContext.match(req.url);
if (!route) {
res.status(404).send('NotFound')
} else {
res.send('Hello world!')
}
}
TypeScript
react-named-router
provides first class typescript type definitions without installing anything else. If you want to
have a custom route configuration object you can extend the CustomNamedRouteConfig
with interface override:
declare module 'react-named-router' {
interface CustomNamedRouteConfig {
title: string
}
}
API
NamedRouter
This is the main component and serves as a provider for the routing in your app. It must be placed inside of your top
level component or within your initial call of ReactDOM.render
.
prop | type | required | description |
---|---|---|---|
routes | NamedRouteConfig[] |
yes | An array of route configuration objects |
routerComponent | React.ComponentType<any> |
React component used as router (BrowserRouter , StaticRouter , etc) |
|
routerProps | any |
Props passed the the router component | |
children | React.ReactNode |
Children elements to render inside router. |
Route name can be an array to have aliases for a same route. Tough only the first name will come up in routing
resolution (like when using match
or when you use NamedRoute
).
NamedLink
Based on the React Router Link component. It uses all the same
props except to
which is configured by Named Router.
prop | type | required | description |
---|---|---|---|
to |
string , object
|
yes | Name of the route used as link target |
params | object |
Object containing key/values to generate parameterized URLs |
NamedLink throw the following errors:
-
Undefined route "$name"
: When theto
prop does not match any route given to theNamedRouter
. -
Route "$name" does not have a path
: When theto
prop points to an existing route which does not have a path (like wrapper routes). -
Missing value for required param "$paramKey"
: When theparams
prop is missing a key/value pair to generate URL.
The to
prop can also take a location object to provide a state. This is useful when redirecting user to login and
keeping somewhere the url from which user was redirected. The location object has the following signature:
location property | type | required | description |
---|---|---|---|
name | string |
yes | Name of the route used as link target |
state | object |
Object containing key/values to keep in location state | |
search | string |
URL query string (eg ?param=1234 ) |
|
hash | string |
URL anchor (eg #section42 ) |
NamedRedirect
Based on the React Router Redirect component. Uses the same
API and errors as NamedLink
.
NamedRoute
Based on the React Router Route component. Only the path
prop
is overridden by Named Router.
prop | type | required | description |
---|---|---|---|
name | string |
yes | Name of the route used for route path generation |
NamedRoute throw the following errors:
-
Undefined route "$name"
: When theto
prop does not match any route given to theNamedRouter
.
Also NamedRoute will forward all fields from matching route to Route component (path
, exact
, component
).
NamedSwitch
Based on the React Router Switch component.
prop | type | required | description |
---|---|---|---|
location | Location |
Location to be used for route matching, defaults to context location |
buildRoutingContext(routes: NamedRouteConfig[], routerContext?: RoutingContextArg, basename?: string)
Utility function that can be used to build the context on server side rendering to get the name of the current route. This is useful when you need to generate meta tags, page title or anything else depending on which route is matching. Can also be used anywhere to convert route names to pathname and vice-versa.
basename
on frontend, be sure to pass it to buildRoutingContext
otherwise context.match
won't
work properly.
The routingContext
argument can be part or the whole context given by React Router or a custom one. It will be used
for location and history properties and also to be able to calculate params.
useParams()
React hook that enables you to quickly retrieve current matching route params (see BaseRoutingContext.params
).
useLocation()
A React hook to retrieve current location. Contains pathname, state, etc.
useCurrentRoute()
This React hook will let you use the currently exact matching route.
Context API
BaseRoutingContext
This object is returned when using withNamedRouting
or useNamedRouting
.
location: Location
Current location used within the router, can be useful to get location.pathname
or location.state
.
params: { [key: string]: string }
Current matching route params. Can be used to get url params from anywhere within the application.
exactParams: { [key: string]: string }
Same as params
but only for exact
routes.
currrentRoute: ExtendedRouteConfig | null
Current matching route (can match only exact paths).
match(pathname: string, matchAll?: boolean)
This function returns the first route matching exactly the given pathname or null if no match found. Also matches only route with exact property. The returned route object is the object from your route configuration plus two additional properties:
-
regex
: a Regular expression to test pathname against for matching. -
parents
: list of parent routes (array of route names).
The matchAll
param allows you to match index routes (non exact routes).
getPath(name: string, params?: object)
Generates path for route matching the given route name. Throws an error if no route is matching. Second argument is used for parameterized URLs generation. Throws also an error if required parameter is missing.
getRoute(name: string)
Retrieve route object for given route name. Throws an error if route is not found.
push(location: string | NamedLocation, params?: object)
Issue a push into the history API (pushState
for HTML5 History) using the given route name and params. Throws the
same errors as getPath
. params
is used to generate path if route has arguments. You can also use a location like
object to provide search
and/or state
.
replace(location: string | NamedLocation, params?: object)
Replaces current location, using history API (replaceState
for HTML5 History) with the given route name and params.
Throws the same errors as getPath
.params
is used to generate path if route has arguments. You can also use a location like
object to provide search
and/or state
.
Contribute
This project is written in TypeScript and uses ESLint as linter to unsure code quality. Tests are written using Jest. Package manager used is Yarn
Run tests:
yarn test
Run build:
yarn build