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

@merzin/router

v0.5.3

Published

Router for web components and vanilla function components.

Readme

Router

Router for web components and vanilla function components.

Defining Routes

Routes are defined using the defineRoute function. Function defineRoute takes two (2) arguments, a pathname or hash and a Factory<HTMLElement>. You can use it to map a pathname or a hash to a web component or a vanilla function component.

Web Component

import { defineRoute } from "@merzin/router";

class LandingPage extends HTMLElement {
  constructor() {
    super();
    this.innerHTML = "<h1>Welcome!</h1>";
  }
}
customElements.define("landing-page", LandingPage);

defineRoute("/", LandingPage);
// You can also use the same instance always by instantiating the component
// defineRoute("/", new LandingPage());

Vanilla Function Component

import { defineRoute } from "@merzin/router";

function LandingPage() {
  const div = document.createElement("div");
  div.innerHTML = "<h1>Welcome!</h1>";
  return div;
}

defineRoute("/", LandingPage);
// You can also use the same instance always by instantiating the component
// defineRoute("/", LandingPage());

Parameterized Routes

You can define parameterized routes using the :param syntax. The parameters object is passed to the component as an argument.

import { defineRoute } from "@merzin/router";

// Parameterized Web Component
class HelloPage extends HTMLElement {
  constructor({ name }: Record<string, string>) {
    super();
    this.innerHTML = `<h1>Hello, ${name}!</h1>`;
  }
}
customElements.define("hello-page", HelloPage);

// Parameterized Vanilla Function Component
function GoodbyePage({ name }: Record<string, string>) {
  const div = document.createElement("div");
  div.innerHTML = `<h1>Goodbye, ${name}!</h1>`;
  return div;
}

defineRoute("/hello/:name", HelloPage);
defineRoute("/goodbye/:name", GoodbyePage);

Note: Make sure you match the trailing slash in the pathname if you want to define a route with a trailing slash. For example, "/hello/:name/" will not match "/hello/John" but will match "/hello/John/".

Modals

Modals are defined by providing a hash instead of a pathname. The hash should only start with "#" and otherwise not include "#" as it is used as a delimiter to allow rendering multiple modals at once.

import { defineRoute } from "@merzin/router";

class LoginModal extends HTMLElement {
  constructor() {
    super();
    this.style.position = "fixed";
    this.innerHTML = "<h1>Login</h1>";
  }
}
customElements.define("login-modal", LoginModal);

defineRoute("#login", LoginModal);

Starting Router

To start the router, you need to call the startRouter function. It takes an optional argument which is the root element where the router will render the components. If not provided, it will use document.body as the root element.

import { startRouter } from "@merzin/router";

startRouter(document.getElementById("root")!);

The optional argument can also be an object with properties viewRoot and modalRoot if you need to render view and modals in different places.

import { startRouter } from "@merzin/router";

startRouter({
  viewRoot: document.querySelector("main")!,
  modalRoot: document.body,
});

Extended History API

This library extends the browser history API to provide a way to listen to URL changes and to manage history stack.

Navigating

You can navigate using browser history API. Anchor links are intercepted and use history.pushState if href is same origin and target is not "_blank".

history.pushState({}, "", "/hello/John");

Pushing to modal stack can be done by appending to the hash.

history.pushState({}, "", location.hash + "#login");

Navigating forwards and backwards can be done with history.forward() and history.back(). This library extends these methods to accept an optional fallback URL. Calling without argument will result in the default behavior.

When calling history.back("/fallback"), it will check if there is a previous history entry with the same origin. If there is, it will navigate to that entry. If not, it will call history.replaceState({}, "", "/fallback") instead. This is useful for ensuring that the user stays on the same page. If you don't want to provide a fallback URL but still want to prevent navigating away from the current origin, you can use history.back(true).

Similarly when calling history.forward("/fallback"), it will check if there is a next history entry with the same origin. If there is, it will navigate to that entry. If not, it will call history.pushState({}, "", "/fallback") instead. Calling history.forward(true) will do nothing if there is no next same origin history entry.

State Change Event

By default we can listen to "popstate" event to detect URL changes. This event is fired when navigating in the history stack using the browser's back and forward buttons, or when calling history.back(), history.forward(), or history.go(). However, there is no event fired when calling history.pushState() or history.replaceState(). This library extends these methods to provide a "statechange" event that is fired always when the URL changes. Class StateChangeEvent contains the following properties:

  • state: The state object passed to history.pushState() or history.replaceState().
  • delta: The amount moved in the history stack. Positive values indicate moving forward, negative values indicate moving backward, and zero indicates no movement i.e. replacement.
  • index: The current index in the history stack.

History Stack

This library extends browser history API with history.stack readonly property. It represents the current session history stack and is of type HistoryStack.

interface HistoryStack {
  index: number; // current position in the items list
  items: HistoryStackItem[]; // list of history items
}

interface HistoryStackItem {
  href: string; // the URL string without origin, i.e. pathname, hash and search
  state: { __index: number; [key: string]: any };
}

As you may notice HistoryStackItem has a __index property. This is injected in history.pushState and history.replaceState calls to allow the router to keep track of the history stack during "popstate" event.

On Back

You can bind a function to be called when the user navigates back in the history stack with onBack(). This function pushes current URL to the stack and whenever user navigates to an entry before it the callback gets called. This is useful for handling cases where you want to perform some action when the user navigates back, such as clearing selection or hiding context menu.

import { onBack } from "@merzin/router/back";

onBack(() => {});

Function onBack() is not recommended for modals as after resolving it leaves a dangling history entry.