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

@pomle/react-router-paths

v0.7.0

Published

React library providing Routing to bridge React and @pomle/paths

Readme

Typed Router for React powered by Paths

Install

This library depends on react @ >=16.8 (hooks support), @pomle/paths @ ^1.3.

yarn add @pomle/paths @pomle/react-router-paths

Usage

This package is similar to React Router, albeit stricter. A decision has been made that path params are considered always required for a path. If you require optional parameters, use two different paths, or implement using query params (see useQueryParams hook).

RouterContext

The React Context that provides routing information to the render tree. Needs a reference to window.history. Allows you to mock History for testing purposes.

import { RouterContext } from '@pomle/react-router-paths';

export function App() {
  return (
    <RouterContext history={window.history}>
      <RestOfYourApp/>
    </RouterContext>
  );
}

PathRoute

PathRoute always takes a render function that is called with a match object. If the match object exists, the URL matched, and the parsed params is available. If it did not match, the match object is null.

Match

An object maybe passed into render function given to PathRoute containing the matched params, and a flag indicating if the path matched exactly, or partly. Partly matching means the actual path was longer than the matching part, and implies there may be better matches. The Match object will only be passed if there was a match, otherwise null is given.

type Match = {
  params: {};
  exact: boolean;
};

When creating transitions between views we need to keep the elements mounted despite there not being a match. Therefore the render function is called regardless if there was a match or not. Transitioning is left to the implementer to decide.

You must return null from the render function if you do not want that path to render something. Use the mount function when uncertain.

import { createPath, codecs } from '@pomle/paths';
import { RouterContext, PathRoute } from '@pomle/react-router-paths';

const paths = {
  books: createPath('/books/:bookId', { bookId: codecs.string }),
};

export function MyRouter() {
  return (
    <RouterContext history={window.history}>
      <PathRoute path={paths.books}>
        {(match) => {
          if (!match) {
            // URL did not match, render nothing.
            return null;
          }

          // URL did match, params are available.
          const { bookId } = match.params;

          // Render page.
          return <BookPage bookId={bookId} />;
        }}
      </PathRoute>
    </RouterContext>
  );
}

mount

The mount function is a utility that will provide the logic from the previous example. It requires the params of the path to match the props of the mounted component.

import { createPath } from '@pomle/paths';
import { RouterContext, PathRoute, mount } from '@pomle/react-router-paths';

const paths = {
  books: createPath('/books/:bookId', { bookId: codecs.string }),
};

export function MyRouter() {
  return (
    <RouterContext history={window.history}>
      <PathRoute path={paths.books}>{mount(BookPage)}</PathRoute>
    </RouterContext>
  );
}

useQueryParams

The hook useQueryParams uses a Query object from @pomle/paths and is similar to useState in that it returns a tuple of existing state, and a function to update state in query params.

import { codecs, createQuery } from '@pomle/paths';
import { useQueryParams } from '@pomle/react-router-paths';

const query = createQuery({
  words: codecs.string,
  numbers: codecs.number,
});

export default function Component() {
  const [params, setParams] = useQueryParams(query);

  return (
    <div>
      <li>Numbers: {params.numbers.join(', ')}</li>
      <li>Words: {params.words.join(', ')}</li>
    </div>
  );
}

params object

The params object contains an array of decoded query param values for each key and codec given to createQuery.

Given the below query;

const query = createQuery({
  words: codecs.string,
  numbers: codecs.number,
});

and the URL being ?words=foo&words=bar&numbers=3, the below useQueryParams hook;

const [params, setParams] = useQueryParams(query);

the params variable will contain;

{
  words: ["foo", "bar"],
  numbers: [3],
}

Array will always be populated for every defined key and contain zero or more elements.

setParams function

The setParams function will take an object of the same shape of params where keys are optional. If key is defined, and array empty, the query params for that key will be removed. If key is not defined, the query params for that key will be untouched.

Examples in the table below may help.

| Query before | setParams call | Query after | | --------------------- | ------------------------------------- | ------------------------- | | ?words=foo | setParams({words: ["bar"]}) | ?words=bar | | ?words=foo | setParams({numbers: [1337]}) | ?words=foo&numbers=1337 | | ?words=foo&number=2 | setParams({numbers: []}) | ?words=foo | | ?words=foo&number=2 | setParams({words: [], numbers: []}) | ? |

You can also consider reading the test suite.

Single param hook ?query=u2

The useQueryParams hook returns the low level API for operating on query string. The following example shows how to create typical implementation that stores the query for a search field.

import { codecs, createQuery } from '@pomle/paths';

export const search = createQuery({
  query: codecs.string,
});

function useQuery(): [string, (text: string) => void] {
  const [params, setParams] = useQueryParams(search);

  const query = params.query[0] ?? '';

  const setQuery = useCallback(
    (query: string) => {
      setParams({
        // Resets to /path instead of /path?query= when empty
        query: query.length > 0 ? [query] : [],
      });
    },
    [setParams],
  );

  return [query, setQuery];
}

export default function MySearch() {
  const [query, setQuery] = useQuery();

  return (
    <form>
      <input
        type='text'
        value={query}
        onChange={(event) => setQuery(event.target.value)}
      />
    </form>
  );
}

useQueryState

Using useQueryState you get the same behavior as useQueryParams, but state is stored syncronously using React's useState, and query search string is updated asynchronously.

This avoids hammering browser history at the cost of delaying state updates that share the same query.

import { codecs, createQuery } from '@pomle/paths';
import { useQueryState } from '@pomle/react-router-paths';

const query = createQuery({
  x: codecs.number,
  y: codecs.number,
});

export default function Component() {
  const [state, setState] = useQueryState(query);

  const pan = {
    x: state.x[0] ?? 0,
    y: state.y[0] ?? 0,
  };

  const handlePointer = useCallback(
    (event: React.PointerEvent) => {
      if (event.buttons === 1) {
        setState({
          x: [event.clientX],
          y: [event.clientY],
        });
      }
    },
    [setState],
  );

  return (
    <div onPointerDown={handlePointer} onPointerMove={handlePointer}>
      <li>Pointer Position: {[pan.x, pan.y].join(',')}</li>
    </div>
  );
}

useNav

With useNav you can create navigation objects that are suitable for <Link> elements, programatically push history, and create navigation callbacks, with type safety.

The useNav call will return an object with three functions. All of them take the path params as arguments.

  • go calls history.push.
  • set calls history.replace.
  • to creates a location object compatible.
  • on returns a function that calls history.push when called.
import { codecs, createPath } from '@pomle/paths';
import { useNav } from '@pomle/react-router-paths';

const paths = {
  books: createPath('/books/:bookId', { bookId: codecs.string }),
};

export default function Component() {
  const nav = {
    books: useNav(paths.books),
  };

  const pushURL = useCallback(() => {
    nav.books.go({ bookId: 'foo' });
  }, []);

  const replaceURL = useCallback(() => {
    nav.books.set({ bookId: 'bar' });
  }, []);

  return (
    <ul>
      <li>
        <button onClick={nav.books.on({ bookId: 'bar' })}>
          Navigate to Bar Book
        </button>
      </li>
      <li>
        <button onClick={pushURL}>Navigate to Foo book URL</button>
      </li>
      <li>
        <button onClick={replaceURL}>Update URL to point to Bar book</button>
      </li>

      {['a', 'b', 'c', 'd'].map((bookId) => {
        return (
          <li>
            <Link to={nav.books.to({ bookId })}>Foo Book</Link>
          </li>
        );
      })}
    </ul>
  );
}

Defining query with createQuery

Using createQuery to define queries allows you to share query information between components. This is useful both when you want to build URLs with query params, and when you want to use the same params in multiple components without prop drilling.