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 🙏

© 2026 – Pkg Stats / Ryan Hefner

lucid-router

v1.5.0

Published

a simple html5-history aware router

Readme

lucid-router

A simple (lucid) html5 history-aware router.

  • ideal for universal/isomorphic apps
  • tiny (adds less than 5kb to your gzipped bundle)
  • progressive enhancement
    • fall back on normal redirect behavior when the history API isn't supported, not hash routing
    • routes can be added and removed at any time, so load the heavier sections of your app later when the user is idle -- if they navigate to it before the bundle loads it'll just redirect like normal

More interested in the philosophy than the configuration? It's at the bottom.

Want to dive straight into some examples?

Configuration

Require the router anywhere it will be used and add routes. Routes are stored inside the lucid-router module, so it is safe to destructure or pass the router's functions around without any binding (see below).

import * as router from 'lucid-router'

router.addRoutes([
  {name: 'profile', path: '/profiles/:profileId'}, // required param
  {name: 'home',    path: '(/:category)'}          // optional param
])

(Note: * as router is required because lucid-router does not have a default export. Most of the time you will only import the parts your module actually needs -- see the ES2015 section below)

You can also set an external property on the route definition. This flag can be a boolean or a function which takes an object containing the route matching info and returns a boolean. false will allow the normal route transition to occur, while true will cancel the location change event and instead navigate the browser directly to the url for a full page load. This setting is mainly for hybrid classic/SPA apps.

router.addRoutes([
  {
    name:     'elsewhere',
    path:     '/non-spa-route',
    external: true | false | (matchInfo => true | false)
  }
])

Also note that routes can be added or removed (see router.removeRoute(name)) at any time, allowing different parts of a larger application to asynchronously inject SPA features into an app as it loads. If a navigation occurs before the necessary route exists, a normal browser redirect will occur.

getLocation returns a RouterLocation object, or null if no routes match. See the Flow types for more info. Calling getLocation with no parameters will look for a window object to pull location info from. If window isn't available (Node.js server) or you need location data for a route you are not currently on, pass the path to match on into getLocation.

router.getLocation()
router.getLocation(path)

Subscribing to location changes

This is where the magic happens. Use this function to call React.render or wrap it with a Flux store so your components can subscribe to changes. Better yet, reserve a spot for it in your global app state and make your whole app a function from state -> UI!

router.register(location => console.log(location))

Callbacks can be registered and un-registered at any time, during an app's lifecycle (think dynamically loaded sections of a large app). Unregister by calling the callback returned from router.register.

Navigating

Use navigate to perform an immediate transition to a given url (a string). Use navigatorFor to build a callback bound to a particular path (equivalent to e => navigate(path, e)):

router.navigate('/path', e)     // => pass the event if you want it cancelled for you
router.navigatorFor('/path')(e) // => same as above, curried style (useful for event binding)

Use navigateToRoute to navigate using a route name and params object:

router.navigateToRoute('profile', {profileId: 5}, e)
router.navigatorForRoute('profile', {profileId: 5})(e)

This functionality is also available without performing a navigation:

router.pathFor('profile', {profileId: 5}) // => returns '/profiles/5'

ES2015 module imports

This is safe and convenient:

import {pathFor, navigatorFor} from 'lucid-router'

const link = pathFor('route-name')
const navigator = navigatorFor(link)

React

Nothing about lucid-router is specific to React, but they make a great pair! I've included a helper component for building anchor tags which you can import and use like so:

import Link from 'lucid-router/link'

class Nav extends React.Component {
  render() {
    return (
      <nav>
        <Link to="my-route" params={{id: 9}}>My Route</Link>
        <Link href="/somewhere/else">Somewhere else</Link>
      </nav>
    )
  }
}

The first becomes an <a> with an href and onClick which defer to pathFor and navigatorForRoute. The second just sets the href with the value provided and calls navigate when clicked. Link is just a shortcut for the most common use cases, so you can use it, ignore it, or make your own!

Philosophy

Another router?? Yup. I see two problems with the status quo. Routers are too specialized and they're treated as some separate, almost magical part of getting a 'real' application built.

Too specialized? What's that mean?

  • Angular Router
  • Angular UI Router
  • Ember Router
  • React Router
  • Express Router
  • Koa Router
  • etc...

Every framework has at least one router dedicated to just that library, plus a few more from the community. I wanted a tool for abstracting the details from routing away from my app, not a tool that depends on the specific library I've chosen to build my app.

The second problem is a little harder to quantify, and it comes from the Rails/Django/MVC patterns of views and layouts. This system tends to create a separation of technology rather than a separation of concerns. A folder for every controller? A folder for every view? No wonder those folders got too big and we needed to abstract common layouts files...

What if we organized it by concern: a folder for accounts, a folder for orders, etc. Then we have a central "app" that decides which of these to sub-apps to hand off to at any given time. What if that central hub of your app didn't have to care about routing logic? At first glance, this may not appear to give you much benefit. You'll still have code somewhere in your app to decide what to render given the current location information.

The difference is that your application now controls this logic, not a router. You can render each screen or state of your app without a router. You can tramsform urls into snapshots of state without an app. You can render the same way on the server and the client. This is the power of abstraction -- simplicity and composability. The "single tool for everything" solutions above sure look powerful at first glance, but in the long run they lock you in to their rules and quirks.

Think of it this way: if you design your app on my router and then decide it sucks, you can replace it with another one or write your own, provided you can convert the replacement's output to simple data.

: ]

Thanks

Thanks to url-pattern for all of the route/pattern work!

Misc

What about router5?

It looks great! There are three reasons I wrote this router anyway:

  1. I didn't know router5 existed when I initially wrote this for a work app, but even if I had...
  2. router5 is more complicated. I love keeping things simple.
  3. I needed really smooth fallback on regular redirects when html5 history isn't supported, not hash routing fallbacks. lucid-router can also be used on non-SPA apps just fine without losing the routing info.. just mark all your routes as external!