npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

isomorphic-app-router

v0.8.1

Published

History-free and dependency-free isomorphic routing library in vanilla JS

Downloads

17

Readme


Getting Started

This package is intended to work seamlessly with history and URLPattern – although URLPattern requires a polyfill at the moment.

npm i -D isomorphic-app-router urlpattern-polyfill

Installation

// <repo>/library/router.ts
import { RouterBuilderFactory, URLPatternResolver } from 'isomorphic-app-router'
import { createBrowserHistory } from 'history'
import "urlpattern-polyfill"

export const history = createBrowserHistory()

export const RouterBuilder = RouterBuilderFactory({
  history,
  resolver: URLPatternResolver,
})
import { …, PathToRegexpResolver } from 'isomorphic-app-router'
import { match } from 'path-to-regexp'

export const RouterBuilder = RouterBuilderFactory({
  …
  resolver: PathToRegexpResolver(match),
})

Your first route

import { RouterBuilder } from '<repo>/library/router'

type Route =
  | { name: 'Home' }
  | { name: 'Product', productId: number }
  | { name: 'NotFound' }

const router = RouterBuilder<Route>()
  // define `Home` route
  .set('home', '/', () => ({ name: 'Home' }))

  // define `Product` route
  .set('product', '/product/:id', ({ params }) => {
    const productId = Number(params.id) // params: { id: string }

    return Number.isNaN(productId)
      ? { name: 'NotFound' }
      : { name: 'Product', productId }
  })

  // finish building the router
  .or(() => ({ name: 'NotFound' })) // required _at the end_

router.makeLinkTo('home') // parameter-less path, no arg required
router.makeLinkTo('product', { id: '2' }) // TS forcefully asks for the route parameters

Path Syntax

I based the library on web standards, namely URLPattern. Which itself based its syntax on path-to-regexp. Therefore, their syntax prevails.

The MDN website is an excellent place to start. Here are a few tips though:

  • /post/* will match /post/, /post/1 & /post/1/2 ; but not /post :warning: To match /post => post{/*}?
  • /post{/:id}? matches /post & /post/1, not /post/1/2
  • Regex groups like /books/(\\d+) can be used but break intellisense of path parameters
  • For nested routers, type the home as {/}? 😉

Recipes / Advanced Usage

Why yet-another X ?

Because I never encountered one that made sense to me:

[!Important] Routing and history are separate concerns.

A history can be unique or cascaded across the client-side app, it should not impact routing.

My opinion: use one history per app.

You want routing? Fine: provide the history to watch changes, you'll get the active route in return.

You want some nested routing? Perfect, provide the history and a base path, you'll get the active route in return.

You want to mix browser, hash and/or memory routing? Fine: provide a different history per-router.

All in pure JS, testable with no framework, adaptable to every framework. Testable: No jsdom needed, no {your framework}-library, no nothing. Aim at that 3ms test 😉.

Fully type-safe and type-driven for mad-typers. It comes with a double-function cost, but still worth it! Now you have the treat of typed path parameters 😛

Contributing

Any contribution is welcome, fork and PR 😁

# clone the repo, then
npm ci
npm run test