
Security News
GitHub Actions Pricing Whiplash: Self-Hosted Actions Billing Change Postponed
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.
@kaliber/routing
Advanced tools
This is a routing library for React.
It has been designed with portability (of components), server side rendering and reverse routing in mind.
Please see the examples directory for working examples.
Create a route map:
import { asRouteMap } from '@kaliber/routing'
export const routeMap = asRouteMap({
home: '',
articles: {
path: 'articles',
list: {
path: '',
data: fetchArticles,
}
article: {
path: ':article',
data: fetchArticle,
},
},
notFound: '*'
})
Pick a route at the server:
import { pick } from '@kaliber/routing'
import { routeMap } from './routeMap'
async function resolve(pathname) {
const result = await pick(pathname,
[routeMap, async (params, route) => ({ status: 200, data: await route.data(match.params) })],
[routeMap.notFound, { status: 404 }],
)
return result
}
Handle the routes in the universal components:
import { LocationProvider } from '@kaliber/routing'
import { routeMap } from './routeMap'
export function UniversalApp({ initialLocation }) {
return (
<LocationProvider {...{ initialLocation, routeMap }} >
<Page />
</LocationProvider>
}
function Page() {
const { matchRoutes } = useRouting()
return matchRoutes(
[routeMap.home, <Home />],
[routeMap.articles, <Articles />],
[routeMap.articles.article, params => <Article {...{ params }} />],
[routeMap.notFound, <NotFound />]
)
}
function Articles() {
return (
<div>
<h1>Articles</h1>
<ul>
<li><Link to={routeMap.articles.article({ articleId: 'article1' })}>Article 1</Link></li>
<li><Link to={routeMap.articles.article({ articleId: 'article2' })}>Article 2</Link></li>
</ul>
</div>
)
}
...
A route map defines the routing structure of the application. It is created by passing a map of routes into the asRouteMap function:
asRouteMap(
{
route1: ...,
route2: ...,
},
{ trailingSlash: true } // reverse route behavior, default is false
)
The rules of asRouteMap is a structure that is similar to the original structure with a few differences:
function that can be called to determine the reverse routepath property (even the routes that you defined as a string)A route can have multiple forms. The simplest is string which simply determines the path of the route:
{ route1: '...' }
In some cases you want to attach data to a route. This library does not make any assumptions about the data of a route. In order to attach data to a route your route definition needs to become an object:
{ route1: { path: '...', data: ... } }
Routes can have children:
{
route1: {
path: '...',
child1: ...,
child2: ...,
}
}
A route path can be a string or an object. When it is a string we make a distinction between the following patterns:
'...': a static pattern that will match exactly':...': a param pattern, the name after the : will be the name of the param'*': a * pattern, this will match anything (including /)Note that route paths can consist of more than one path segment, a few examples:
'something/:param' - A route that will match '/something/abc' with param set to 'abc''something/* - A route that will match '/something/abc/def' with * set to 'abc/def'':param/something' - A route that will match '/abc/something' with param set to 'abc'Route paths can also be objects, this allows you to use different paths for different languages:
route1: {
path: ':language',
route2: {
path: { en: 'english', nl: 'dutch' }
}
}
The name of a parent routes' path param should be set to language in order for this pattern to work. If you want to use a different name you need to provide this as configuration to the route map:
asRouteMap(
{
...
},
{ languageParamName: 'locale' }
)
After converting the object to a route map, the routes have become functions that can be used to determine the reverse route.
const map = asRouteMap({
route1: {
path: 'route1',
route2: ':route2'
}
})
console.log(map.route1()) // "/route1"
console.log(map.route1.route2({ route2: 'route2' })) // "/route1/route2"
Note that you can force the reverse routes to have a trailing slash with the option trailingSlash set to true:
asRouteMap(
{
...
},
{ trailingSlash: true }
)
There are a few methods used for matching routes, some are used on the client, others at the server side.
pickRoutepickuseRouting (with matchRoute and matchRoutes)useLocationMatchusePick (with pick)pickRoutefunction pickRoute(pathname: string, routeMap: RouteMap): { params: object, route: Route } | null
Picks a Route from the RouteMap and returns it together with a params object if matched. If no route was matched this method returns null.
pickfunction pick(pathname: string,
[routeMap: RouteMap, defaultHandler: A | (params, route) => A],
...overrides: Array<[route: Route, handler: B | (params, route) => B]>
): A | B
Convenience function that allows you to perform easy overrides of specific routes in a structured fashion. An example:
return pick(location.pathname,
[routeMap, { status: 200 }],
[routeMap.notFound, { status: 404 }],
)
useRoutingfunction useRouting(): {
matchRoute: (route: Route, handler: A | (params) => A) => A,
matchRoutes: (...routes: Array<[route: Route, handler: A | (params) => A]>) => A,
}
Mainly used inside the render tree. Allows you to render based on a matched route.
useLocationMatchfunction useLocationMatch(): { params: object, route: Route } | null
Similar to pickRoute it returns the matched Route with it's params when a match was found, null otherwise. A small difference is that the returned route has the params partially applied to its reverse route function. This means that you do not need to supply any parameters that would be required by any parent routes.
usePickfunction usePick(): (...routes: Array<Route>) => Route
Returns a function that lets you choose a route from an array of routes, or null if nothing matched. The selected route is found by traversing the parents of the picked route (useLocationMatch).
Linkfunction Link({
to: string,
replace: boolean,
state: object,
anchorProps: object,
children,
})
This is essentially an <a href="..."> that uses the history API. The anchorProps are directly set on the a element.
If you want to prevent the default click handling in certain situations you can supply anchorProps.onClick and call event.preventDefault() from the event handler.
useNavigatefunction useNavigate(): (to: number | string, { state: object, replace?: boolean }) => void
Allows you to navigate without using the Link component. Note that a call to the resulting function will not work when rendering on the server.
LocationProviderfunction LocationProvider({
basePath: string,
initialLocation: { pathname: string, search: string, hash: string },
routeMap: RouteMap,
children,
})
This provides the context for all of the routing related hooks. It detects the difference between client and server side rendering: if window is undefined it will use the initialLocation for the match.
StaticLocationProviderfunction StaticLocationProvider({
location: { pathname: string, search: string, hash: string },
children,
})
This provides a static location context for all of the routing related hooks of its children. It can be used to render content based on a location that is not the current location. This is useful for animations.
asRouteChainfunction asRouteChain(route: Route): Array<Route>
Returns an array of all routes from the root of the route map up to (and including) the given route. This can be useful when rendering on the server and loading all required data.
useLocationfunction useLocation(): { pathname: string, search: string, hash: string, state?: object }
Returns the current location.
useHistoryfunction useHistory(): { location, listen(listener), navigate(to, { state, replace }) }
Returns a reference to the history wrapper. Note that the resulting object can not be used in a non browser context. Also note that the navigate function here ignores the basePath.
Why would we create a new routing library?
Back in the day there were 2 popular choices for React: 'React Router' and 'Reach Router'. We went for Reach Router because it was using relative routes. Relative routes fit better with the component model where parents can know about children, but children can not know about parents.
Most popular React routing libraries use JSX to define routes, we don't like using JSX to define routes as it creates a lot of noise or causes linting errors (in case of things like a path property on the component).
When working in a 'universal' environment where server side rendering takes place it helps if the same route structure can be used on the server and the client. On the server you want to return a 404 for some resource that is not found, on the client you want to display the correct NotFound page.
Reverse routing is missing in most routing libraries.
FAQs
Provides routing for React
We found that @kaliber/routing demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 19 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
GitHub postponed a new billing model for self-hosted Actions after developer pushback, but moved forward with hosted runner price cuts on January 1.

Research
Destructive malware is rising across open source registries, using delays and kill switches to wipe code, break builds, and disrupt CI/CD.

Security News
Socket CTO Ahmad Nassri shares practical AI coding techniques, tools, and team workflows, plus what still feels noisy and why shipping remains human-led.