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

@rectify-dev/router

v2.5.0

Published

Client-side router for Rectify — inspired by React Router v6

Readme

@rectify-dev/router

npm License: MIT

Client-side router for Rectify — inspired by React Router v6. Provides BrowserRouter, HashRouter, nested routes, Outlet, Link, NavLink, and a full set of navigation hooks.


Installation

# npm
npm install @rectify-dev/core @rectify-dev/router

# pnpm
pnpm add @rectify-dev/core @rectify-dev/router

# yarn
yarn add @rectify-dev/core @rectify-dev/router

Setup

Vite

import { defineConfig } from "vite";
import rectify from "@rectify-dev/vite-plugin";

export default defineConfig({
  plugins: [rectify()],
});

TypeScript

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@rectify-dev/core"
  }
}

Quick start

import { createRoot } from "@rectify-dev/core";
import { BrowserRouter } from "@rectify-dev/router";
import App from "./App";

createRoot(document.getElementById("root")!).render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
);
import { Routes, Route, Link } from "@rectify-dev/router";
import Home from "./pages/Home";
import About from "./pages/About";
import User from "./pages/User";

export default function App() {
  return (
    <>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users/:id" element={<User />} />
        <Route path="*" element={<p>404 Not Found</p>} />
      </Routes>
    </>
  );
}

API Reference

BrowserRouter

Provides routing backed by the HTML5 History API (pushState / popstate). URLs look like /about, /users/42.

type BrowserRouterProps = {
  children?: any;
  basename?: string; // default "/"
};
<BrowserRouter basename="/app">
  <App />
</BrowserRouter>

Use basename when your app is served from a sub-path (e.g. /app).


HashRouter

Provides routing backed by window.location.hash. URLs look like /#/about. No server configuration required.

type HashRouterProps = {
  children?: any;
  basename?: string;
};
<HashRouter>
  <App />
</HashRouter>

Routes and Route

<Routes> renders the first <Route> whose path matches the current URL. <Route> itself renders null — it is a data carrier read by <Routes>.

type RouteProps = {
  path?:     string;    // URL pattern, e.g. "/users/:id"
  index?:    boolean;   // matches the parent path exactly
  element?:  RectifyNode;
  children?: RectifyNode; // nested <Route>s
};
<Routes>
  {/* exact match */}
  <Route path="/" element={<Home />} />

  {/* URL param */}
  <Route path="/users/:id" element={<UserProfile />} />

  {/* catch-all */}
  <Route path="*" element={<NotFound />} />

  {/* layout route with nested children */}
  <Route path="/settings" element={<SettingsLayout />}>
    <Route index element={<GeneralSettings />} />
    <Route path="profile" element={<ProfileSettings />} />
  </Route>
</Routes>

Outlet

Renders the matched child route's element inside a layout component.

import { Outlet } from "@rectify-dev/router";

function SettingsLayout() {
  return (
    <div>
      <aside>
        <NavLink to="/settings">General</NavLink>
        <NavLink to="/settings/profile">Profile</NavLink>
      </aside>
      <main>
        <Outlet /> {/* child route renders here */}
      </main>
    </div>
  );
}

Link

Client-side navigation anchor. Intercepts plain left-clicks to update the URL without a full page reload. Modified clicks (Ctrl / Cmd / Shift / Alt) and middle-button clicks are passed through.

type LinkProps = {
  to:        string;
  replace?:  boolean;
  state?:    unknown;
  children?: RectifyNode;
  className?: string;
  style?:    CSSProperties;
  onClick?:  (e: SyntheticMouseEvent) => void;
};
<Link to="/about">About</Link>
<Link to="/checkout" replace state={{ from: "/cart" }}>Checkout</Link>

NavLink

Like Link but applies activeClassName when the current path matches to.

type NavLinkProps = LinkProps & {
  activeClassName?: string; // default "active"
  end?:             boolean; // default true
};
{/* exact match */}
<NavLink to="/settings">Settings</NavLink>

{/* active on /blog and any sub-path */}
<NavLink to="/blog" end={false} activeClassName="current">
  Blog
</NavLink>

Navigate

Renders null but triggers a navigation as a side-effect. Useful for redirect-on-render patterns.

type NavigateProps = {
  to:       string;
  replace?: boolean;
  state?:   unknown;
};
function ProtectedRoute({ isLoggedIn }: { isLoggedIn: boolean }) {
  if (!isLoggedIn) {
    return <Navigate to="/login" replace />;
  }
  return <Dashboard />;
}

useNavigate

Returns the navigate function for programmatic navigation.

type NavigateFunction = {
  (to: string, options?: NavigateOptions): void;
  (delta: number): void;  // go(-1) = back, go(1) = forward
};

type NavigateOptions = {
  replace?: boolean;
  state?:   unknown;
};
const navigate = useNavigate();

// push
navigate("/dashboard");

// replace
navigate("/login", { replace: true });

// go back
navigate(-1);

useLocation

Returns the current RouterLocation. Re-renders whenever the location changes.

type RouterLocation = {
  pathname: string;
  search:   string;  // includes "?" e.g. "?q=hello"
  hash:     string;
  state:    unknown;
  key:      string;
};
const location = useLocation();
console.log(location.pathname); // e.g. "/users/42"

useParams

Returns URL params extracted by the nearest matching <Route>.

// Route: <Route path="/users/:id/posts/:postId" element={<Post />} />

const { id, postId } = useParams<{ id: string; postId: string }>();

useMatch

Tries to match a pattern against the current pathname. Returns a PathMatch on success, or null.

type PathMatch = {
  params:   Record<string, string>;
  pathname: string; // matched portion
};
const match = useMatch("/users/:id");
if (match) {
  console.log(match.params.id);
}

useSearchParams

Returns [URLSearchParams, setter]. Calling the setter navigates to the current path with a new query string.

const [params, setParams] = useSearchParams();

// read
console.log(params.get("q"));

// write — navigates to ?q=hello&page=1
setParams({ q: "hello", page: "1" });

useHref

Resolves a to path to a full href string respecting the router's basename and the current location.

const href = useHref("/about"); // "/about" with BrowserRouter, "/#/about" with HashRouter

URL patterns

| Pattern | Matches | |---------|---------| | /about | Exactly /about | | /users/:id | /users/42, /users/alice | | /files/* | /files/, /files/a/b/c | | * | Everything (catch-all) |


Nested routes example

function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* Layout route */}
        <Route path="/" element={<RootLayout />}>
          <Route index element={<Home />} />
          <Route path="about" element={<About />} />

          {/* Nested layout */}
          <Route path="users" element={<UsersLayout />}>
            <Route index element={<UserList />} />
            <Route path=":id" element={<UserProfile />} />
          </Route>
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

License

MIT