named-urls
v2.0.1
Published
Simple named url patterns in Javascript
Downloads
24,540
Readme
Simple named url patterns in JavaScript.
Implementing a static route config and named routes on top of (react-router) at this point is like a 20 line ordeal. – Ryan Florence
Motivation
Named routes are essential to keep route config DRY and prevent silly errors
due to typos. This feature was removed from react-router
in 1.0 and I missed
it since then as many
others.
There're other libs dealing with named routes, some of them provide custom
Link
, Route
routes, some of them have more features to integrate with
express
. Here's incomplete list of libs I considered before writing these
20 lines of code (and 200+ lines of other files to publish this package):
- https://github.com/adamziel/react-router-named-routes
- https://github.com/taion/use-named-routes
- https://github.com/alubbe/named-routes
NOTE: v2 introduces breaking changes. Please check out the migration guide before upgrading.
Installation
$ npm install named-urls
or
$ yarn add named-urls
Quickstart
Create file with all routes in your application (e.g. routes.js
). Use
named-urls/include
to create namespaced group of routes with common prefix:
// routes.js
import { include } from 'named-urls'
export default {
// simple route
profile: '/profile',
// route with params
article: '/article/:articleId',
// route with optional params
messages: '/messages/:messageId?',
// Routes with common path prefix
auth: include('/auth', {
// Absolute url (ignore /auth prefix)
login: '/login/',
// Relative urls (prefixed with /auth)
passwordReset: 'password/reset/',
passwordVerify: 'password/verify/',
}),
// Routes with params
messages: include('/messages', {
all: '',
unread: 'unread/',
// nesting of includes is allowed
detail: include(':messageId/', {
show: '',
edit: 'edit/',
comments: 'comments/',
})
})
}
Use routes in Route
component from react-router-dom
:
// App.js
import React from 'react'
import { Switch, Route } from 'react-router-dom'
import routes from './routes'
import * as scenes from './scenes' // just a convention, could be "pages"
function App() {
return (
<Switch>
<Route path={routes.profile} component={scenes.Profile} />
<Route path={routes.auth.login} component={scenes.auth.Login} />
// ...
<Route path={routes.messages.unread} component={scenes.messages.Unread} />
<Route path={routes.messages.detail.show} component={scenes.messages.Detail} />
</Switch>
)
}
Routes with parameters can be formatted using reverse
function:
// Navigation.js
import React from 'react'
import { Link } from 'react-router'
import { reverse } from 'named-urls'
function Navigation({ messages }) {
return (
<ul>
<li><Link to={`${routes.profile}`}>Profile</Link></li>
// ...
// Use reverse to replace params in route pattern with values
{messages.map(message =>
<li key={message.id}>
<Link to={reverse(`${routes.messages.detail.show}`, { messageId: message.id })}>
Profile
</Link>
</li>
)}
</ul>
)
}
Ending slash
Patterns ending with slash are always reversed to URL with ending slash and vice versa: Paterns without ending slash are always reserved to URL without endlish slash:
// pattern with ending slash
reverse('pattern/:optional?', { optional: 42 }) // pattern/42
reverse('pattern/:optional?') // pattern
// pattern without ending slash
reverse('pattern/:optional?/', { optional: 42 }) // pattern/42/
reverse('pattern/:optional?/') // pattern/
Migrating from v1.x.x to v2.x.x
For better compatibility with React Router, v2 uses path-to-regexp to resolve URLs. This means some of your routes may break when you upgrade.
reverse
-reverse('pattern/page:param?', {})
+reverse('pattern/(page:param)?', {})
reverseForce
-reverseForce('pattern/page:param?', {})
+reverseForce('pattern/(page:param)?', {})
To get a full overview of all accepted patterns, consult the path-to-regexp documentation.
Some tricks
Using an "include" route
If you define a route as an include, calling it directly will return you a function. To by-pass that, you have a solution to create an empty route inside, let's call it self
:
// routes.js
import { include } from 'named-urls'
export default {
messages: include('/messages', {
self: '',
detail: include(':messageId/', {
show: '',
edit: 'edit/',
comments: 'comments/',
})
})
}
so you'll be able to do:
<Route path={routes.messages.self} component={Messages} />
A way to not define a useless route is to use the string way of a route like that:
// routes.js
import { include } from 'named-urls'
export default {
messages: include('/messages', {
detail: include(':messageId/', {
edit: 'edit/',
comments: 'comments/',
})
})
}
<Route path={`${routes.messages}`} component={Messages} />
// OR
<Route path={String(routes.messages)} component={Messages} />