Typed Route Config
Type safe configuration for routes.
Installation
npm install typed-route-config
The Problem
If you use react-router, you might want to centralise your routes in some kind of configuration object, which can then be looked up in all the places you use them.
For example, you may have:
import React from 'react'
import { BrowserRouter, Switch, Route, Link } from 'react-router-dom'
export const App: React.FC = () => (
<BrowserRouter>
<Switch>
<Route path={'/dashboard/me'} render={() => <div>the dashboard profile component here...</div>}/>
<Route path={'/dashboard/offer/:offerId/documents'} render={() => <div>the documents component here...</div>}/>
</Switch>
</BrowserRouter>
)
type LinkProps = {
offerId: string
}
export const LinkToDocuments: React.FC<LinkProps> = ({ offerId }) => (
<Link to={`/dashboard/offer/${offerId}/documents`} />
)
So, suppose you to factor these routes out into a configuration that looked like this:
export const mySimpleRouteConfig = {
'dashboard': '/dashboard',
'dashboard.me': '/dashboard/me',
'dashboard.offer': '/dashboard/offer/:offerId',
'dashboard.offer.collaborators': '/dashboard/offer/:offerId/collaborators',
'dashboard.offer.documents': '/dashboard/offer/:offerId/documents',
}
Which would then look something like this:
import React from 'react'
import { BrowserRouter, Switch, Route, Link } from 'react-router-dom'
import { mySimpleRouteConfig } from './myRoutes'
export const App: React.FC = () => (
<BrowserRouter>
<Switch>
<Route path={mySimpleRouteConfig['dashboard.me']} render={() => <div>the dashboard profile component here...</div>}/>
<Route path={mySimpleRouteConfig['dashboard.offer.documents']} render={() => <div>the documents component here...</div>}/>
</Switch>
</BrowserRouter>
)
type LinkProps = {
offerId: string
}
export const LinkToDocuments: React.FC<LinkProps> = ({ offerId }) => {
const documentsPath = mySimpleRouteConfig['dashboard.offer.documents']
const documentsUrlForThisOfferId = interpolate(documentsPath, { offerId })
return (
<Link to={documentsUrlForThisOfferId} />
)
}
The issue here is you have no typesafety with how you've implented the interpolate
function!
The Solution
Using typed-route-config
could re-write your config as:
import { createRoutes } from 'typed-route-config';
export const { route, path } = createRoutes(root => ({
'dashboard': root.path('dashboard'),
'dashboard.me': root.path('dashboard/me'),
'dashboard.offer': root.path('dashboard/offer').param('offerId'),
'dashboard.offer.collaborators': root.path('dashboard/offer').param('offerId').path('collaborators'),
'dashboard.offer.documents': root.path('dashboard/offer').param('offerId').path('documents')
}))
And then continuing with our example, we would then get
import React from 'react'
import { BrowserRouter, Switch, Route, Link } from 'react-router-dom'
import { route, path } from './myRoutes'
export const App: React.FC = () => {
return (
<BrowserRouter>
<Switch>
<Route path={path('dashboard.me')} render={() => <div>the dashboard profile component here...</div>}/>
<Route path={path('dashboard.offer.documents')} render={() => <div>the documents component here...</div>}/>
</Switch>
</BrowserRouter>
)
}
type LinkProps = {
offerId: string
}
export const LinkToDocuments: React.FC<LinkProps> = ({ offerId }) => {
const documentsUrlForThisOfferId = route('dashboard.offer.documents', { offerId })
return (
<Link to={documentsUrlForThisOfferId} />
)
}
We can go a step further and neaten up our route config to remove the repetition.
You can use the .group(...)
to nest routes. The config from before could be re-written as:
import { createRoutes } from 'typed-route-config';
export const { routes, route, path } = createRoutes(root => ({
'dashboard': root.path('dashboard').group(dashboard => ({
'': dashboard,
'me': dashboard.path('me'),
'offer': dashboard.path('offer').param('offerId').group(offer => ({
'': offer,
'collaborators': offer.path('collaborators'),
'documents': offer.path('documents')
}))
})),
}))
Usage
Define your routes somewhere in your application using the createRoutes
function:
import { createRoutes, MakeRouteParams } from 'typed-route-config'
const { routes, route, path } = createRoutes(root => ({
}))
export { route, path }
type Routes = typeof routes
export type RouteNames = keyof Routes
export type RouteParams<N extends RouteNames> = MakeRouteParams<Routes, N>
TODO explain the rest of the API
Building from source
To build, just run npm run build
To make a change
Make your change, (don't forget to npm run build
), then increment the version number in the package.json before pushing.
In the projects which depend on this package, run npm update typed-route-config
.
npm detects changes using the version number in the package.json