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

@molecule/app-routing-react-router

v1.1.3

Published

React Router v7 provider for @molecule/app-routing

Readme

@molecule/app-routing-react-router

React Router (v7/v8) provider for @molecule/app-routing.

This package provides a React Router implementation of the molecule Router interface, allowing you to use molecule's routing abstractions with React Router.

Paginated or filtered page? Use useListRoute for its URL state. It reads page, filters and selected views from the current URL; builds links that retain the current path/filters; and resets page when a filter or view changes. Declare path variants explicitly instead of translating API filters into browser URLs. It works under ordinary BrowserRouter without a data router or an extra provider.

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Quick Start

import { BrowserRouter, Routes, Route } from 'react-router'
import { MoleculeRouterProvider } from '@molecule/app-routing-react-router'

function App() {
  // MoleculeRouterProvider bonds the adapter automatically (calls setRouter in an
  // effect on mount), so @molecule/app-routing's navigate()/getRouter() drive THIS
  // real React Router — no manual setRouter wiring needed.
  return (
    <BrowserRouter>
      <MoleculeRouterProvider>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/users/:id" element={<UserProfile />} />
        </Routes>
      </MoleculeRouterProvider>
    </BrowserRouter>
  )
}

A list with query-selected views. The public URL remains public after login; a protected view waits for session restoration. Include the signed-in principal in the data key so one user's cached response cannot appear for another user, and pass null while the session is still restoring (status === 'loading') — the auth gate holds the request until then anyway, so waiting for it costs nothing.

import { Link } from 'react-router'
import { useAuth, useHttpClient, useTranslation } from '@molecule/app-react'
import { unwrapList } from '@molecule/app-http'
import { useQuery } from '@molecule/app-query-react'
import { useListRoute } from '@molecule/app-routing-react-router'

interface Entry {
  id: string
  title: string
}

function ActivityList() {
  const { status, user } = useAuth<{ id: string }>()
  const http = useHttpClient()
  const { t } = useTranslation()
  const list = useListRoute({
    pageSize: 10,
    filters: { category: { query: 'category' }, sort: { defaultValue: 'recent' } },
    selections: {
      public: { path: '/activity', query: { view: undefined } },
      personal: { path: '/activity', query: { view: 'personal' }, requiresAuth: true },
    },
    auth: { status, redirectTo: '/sign-in' },
  })
  const entries = useQuery(
    list.canLoad && status !== 'loading'
      ? {
          key: ['activity', user?.id ?? 'anonymous', list.selection, list.filters, list.page],
          fetch: async (signal) =>
            unwrapList<Entry>(
              await http.get('/api/activity', {
                signal,
                params: {
                  ...list.filters,
                  view: list.selection,
                  limit: list.pageSize,
                  offset: list.offset,
                },
              }),
            ),
        }
      : null,
  )
  return (
    <>
      <Link to={list.href({ selection: 'public' })}>
        {t('activity.public', undefined, { defaultValue: 'Public activity' })}
      </Link>
      {user && (
        <Link to={list.href({ selection: 'personal' })}>
          {t('activity.personal', undefined, { defaultValue: 'Your activity' })}
        </Link>
      )}
      <select
        value={list.filters.sort}
        onChange={(event) => list.setFilters({ sort: event.target.value })}
      >
        <option value="recent">
          {t('activity.recent', undefined, { defaultValue: 'Recent' })}
        </option>
        <option value="oldest">
          {t('activity.oldest', undefined, { defaultValue: 'Oldest' })}
        </option>
      </select>
      {entries.data?.map((entry) => (
        <p key={entry.id}>{entry.title}</p>
      ))}
      <Link to={list.href({ page: list.page + 1 })}>
        {t('activity.next', undefined, { defaultValue: 'Next page' })}
      </Link>
    </>
  )
}

Path-selected views and path-backed filters use the same hook. The browser route contract determines the URL; API filters can be derived separately from params.

// Mount the same page on /members/:member and /members/:member/saved.
const list = useListRoute({
  selections: {
    authored: { path: '/members/:member' },
    saved: { path: '/members/:member/saved' },
  },
})
// <Link to={list.href({ selection: 'saved' })}>…</Link> retains :member.
// list.href({ page: 2 }) stays on /members/:member/saved?page=2.
// Use list.params.member in the API request; it is not another query key.

// On /topics/:topic, changing page keeps the encoded topic in the path.
const topics = useListRoute({
  path: '/topics/:topic',
  filters: { topic: { param: 'topic' }, sort: { defaultValue: 'recent' } },
})
// topics.setFilters({ topic: 'type systems' }) changes the path and resets page.
// PaginationBar can use page={topics.page}, pageSize={topics.pageSize},
// onPageChange={topics.setPage}; custom links use topics.href({ page: next }).

Type

provider

Installation

npm install @molecule/app-routing-react-router @molecule/app-i18n @molecule/app-routing react react-router
npm install -D @types/react

API

Interfaces

MoleculeRouterProviderProps

Provider props.

interface MoleculeRouterProviderProps {
  /**
   * Child components.
   */
  children: ReactNode

  /**
   * Optional route definitions for named routes.
   */
  routes?: RouteDefinition[]

  /**
   * Callback when router is ready.
   */
  onRouterReady?: (router: Router) => void
}

ListRouteFilter

Where a list filter lives in the URL. Path values take precedence over query values.

interface ListRouteFilter {
  /** Query key; defaults to the filter's name when `param` is absent. */
  query?: string
  /** Path parameter supplied by the current route, such as `category` in `/topics/:category`. */
  param?: string
  /** Value returned when neither URL source supplies a value; omitted from generated queries. */
  defaultValue?: string
}

ListRouteSelection

A selectable list view, expressed in the app's own route contract.

interface ListRouteSelection {
  /** Absolute route pattern, for example `/members/:member/saved`. */
  path: string
  /** Query values identifying this view. An undefined value requires the key to be absent. */
  query?: QueryParams
  /** A protected selection never loads before authentication is confirmed. */
  requiresAuth?: boolean
}

ListRouteAuth

Authentication readiness for protected list selections.

interface ListRouteAuth {
  /** The auth client's status. Unavailable is distinct from a signed-out session. */
  status: 'loading' | 'authenticated' | 'unauthenticated' | 'unavailable'
  /** App-owned sign-in URL. A signed-out protected visit replaces history with this URL. */
  redirectTo: string
}

ListRouteOptions

URL-backed list state configuration; it neither invents nor rewrites the app's route contract.

interface ListRouteOptions<Filter extends string = string, Selection extends string = string> {
  /** Fixed number of items per page; defaults to 20. */
  pageSize?: number
  /** Query key for a 1-indexed page; defaults to `page`. Page one omits the key. */
  pageParam?: string
  /** Route pattern for changing path-backed filters when no selections are configured. */
  path?: string
  /** Filters and their URL sources. API-only filters do not belong here. */
  filters?: Record<Filter, ListRouteFilter>
  /** Named views sharing a list. Both path variants and query variants are supported. */
  selections?: Record<Selection, ListRouteSelection>
  /** Tie-breaker when multiple selections match, or fallback when none matches. */
  defaultSelection?: NoInfer<Selection>
  /** Required when a selection is protected. Public views remain public as auth changes. */
  auth?: ListRouteAuth
}

ListRouteChange

A single atomic list navigation. Filter/selection changes reset page unless explicitly supplied.

interface ListRouteChange<Filter extends string = string, Selection extends string = string> {
  /** Target page, starting at one. */
  page?: number
  /** Patch of URL filter values. Undefined removes a query filter. */
  filters?: Partial<Record<Filter, string | undefined>>
  /** Target view, resolved through its configured path and query values. */
  selection?: Selection
  /** Optional path parameter changes, for example when opening a different member's saved view. */
  params?: RouteParams
}

ListRouteResult

Reactive list state and navigation, derived directly from React Router's current location.

interface ListRouteResult<Filter extends string = string, Selection extends string = string> {
  /** Current 1-indexed page; malformed and non-positive URL values read as page one. */
  page: number
  /** Configured items per page. */
  pageSize: number
  /** Zero-indexed item offset for an API request. */
  offset: number
  /** Current URL filter values, including values supplied by path parameters. */
  filters: Record<Filter, string | undefined>
  /** Matching view, or the configured fallback. */
  selection: Selection | undefined
  /** Decoded current path parameters, also usable as API-only filters. */
  params: RouteParams
  /** False while a protected selection is awaiting auth, signed out, or unavailable. */
  canLoad: boolean
  /** Readiness for the current view. A public view always stays ready. */
  status: 'ready' | 'loading' | 'unauthenticated' | 'unavailable'
  /** Sign-in destination while a signed-out protected view is being redirected. */
  redirectTo: string | undefined
  /** Build a real link target, preserving the current pathname, unrelated query values and hash. */
  href: (change?: ListRouteChange<Filter, Selection>) => string
  /** Navigate atomically. Location state is preserved unless options explicitly replace it. */
  navigate: (change: ListRouteChange<Filter, Selection>, options?: NavigateOptions) => void
  /** Change only the page, retaining the current filters and view. */
  setPage: (page: number, options?: NavigateOptions) => void
  /** Patch URL filters and return to page one. */
  setFilters: (
    filters: Partial<Record<Filter, string | undefined>>,
    options?: NavigateOptions,
  ) => void
  /** Select a configured view at page one, preserving current route parameters. */
  select: (selection: Selection, options?: NavigateOptions) => void
}

ReactRouterConfig

React Router-specific configuration.

interface ReactRouterConfig {
  /**
   * React Router navigate function (from useNavigate).
   */
  navigate?: NavigateFunction

  /**
   * Current location (from useLocation).
   */
  location?: {
    pathname: string
    search: string
    hash: string
    state?: unknown
    key?: string
  }

  /**
   * Current params (from useParams).
   */
  params?: Record<string, string | undefined>

  /**
   * Initial route definitions.
   */
  routes?: RouteDefinition[]
}

ReactRouterHooks

React Router hooks adapter.

interface ReactRouterHooks {
  /**
   * The navigate function from useNavigate.
   */
  navigate: NavigateFunction

  /**
   * Location from useLocation.
   */
  location: {
    pathname: string
    search: string
    hash: string
    state?: unknown
    key?: string
  }

  /**
   * Params from useParams.
   */
  params: Record<string, string | undefined>

  /**
   * Search params from useSearchParams.
   */
  searchParams: URLSearchParams
}

RouteLocation

Current URL decomposed into pathname, search string, hash, navigation state, and unique key.

interface RouteLocation {
  /**
   * Current pathname.
   */
  pathname: string
  /**
   * Query string (including leading ?).
   */
  search: string
  /**
   * Hash (including leading #).
   */
  hash: string
  /**
   * State data passed with navigation.
   */
  state?: unknown
  /**
   * Unique key for this location.
   */
  key?: string
}

RouteMatch

Result of matching a URL against a route pattern (path, params, query string).

interface RouteMatch<Params extends RouteParams = RouteParams> {
  /**
   * Route path pattern.
   */
  path: string
  /**
   * Matched URL pathname.
   */
  pathname: string
  /**
   * Route parameters.
   */
  params: Params
  /**
   * Whether this is an exact match.
   */
  isExact: boolean
}

NavigateOptions

Options for programmatic navigation (replace vs push, carry state, preserve query/hash).

interface NavigateOptions {
  /**
   * Replace current history entry instead of pushing.
   */
  replace?: boolean
  /**
   * State to pass with navigation.
   */
  state?: unknown
  /**
   * Preserve current query params.
   */
  preserveQuery?: boolean
  /**
   * Preserve current hash.
   */
  preserveHash?: boolean
}

RouteDefinition

Route configuration entry (path pattern, name, auth requirements, roles, children).

interface RouteDefinition {
  /**
   * Route path pattern.
   */
  path: string
  /**
   * Route name (for named routes).
   */
  name?: string
  /**
   * Whether the route requires exact matching.
   */
  exact?: boolean
  /**
   * Whether the route requires authentication.
   */
  requiresAuth?: boolean
  /**
   * Required roles/permissions.
   */
  roles?: string[]
  /**
   * Route metadata.
   */
  meta?: Record<string, unknown>
  /**
   * Child routes.
   */
  children?: RouteDefinition[]
}

Router

Client-side router providing navigation, guards, route matching, and history control.

All routing providers must implement this interface.

interface Router {
  /**
   * Returns the current route location (pathname, search, hash, state).
   */
  getLocation(): RouteLocation
  /**
   * Gets the current route params.
   */
  getParams<T extends RouteParams = RouteParams>(): T
  /**
   * Gets the current query params.
   */
  getQuery(): QueryParams
  /**
   * Gets a specific query parameter.
   */
  getQueryParam(key: string): string | undefined
  /**
   * Gets the current hash.
   */
  getHash(): string
  /**
   * Navigates to a path.
   */
  navigate(path: string, options?: NavigateOptions): void
  /**
   * Navigates to a named route.
   */
  navigateTo(
    name: string,
    params?: RouteParams,
    query?: QueryParams,
    options?: NavigateOptions,
  ): void
  /**
   * Goes back in history.
   */
  back(): void
  /**
   * Goes forward in history.
   */
  forward(): void
  /**
   * Goes to a specific point in history.
   */
  go(delta: number): void
  /**
   * Updates the current query params.
   */
  setQuery(params: QueryParams, options?: NavigateOptions): void
  /**
   * Updates a specific query parameter.
   */
  setQueryParam(key: string, value: string | undefined, options?: NavigateOptions): void
  /**
   * Updates the current hash.
   */
  setHash(hash: string, options?: NavigateOptions): void
  /**
   * Checks if a path matches the current location.
   *
   * @returns `true` if the path matches the current route.
   */
  isActive(path: string, exact?: boolean): boolean
  /**
   * Matches a path pattern against a pathname.
   */
  matchPath<Params extends RouteParams = RouteParams>(
    pattern: string,
    pathname: string,
  ): RouteMatch<Params> | null
  /**
   * Generates a URL from a named route.
   */
  generatePath(name: string, params?: RouteParams, query?: QueryParams): string
  /**
   * Subscribes to route changes.
   */
  subscribe(listener: RouteChangeListener): () => void
  /**
   * Adds a navigation guard.
   */
  addGuard(guard: NavigationGuard): () => void
  /**
   * Registers route definitions.
   */
  registerRoutes(routes: RouteDefinition[]): void
  /**
   * Gets all registered routes.
   */
  getRoutes(): RouteDefinition[]
  /**
   * Destroys the router.
   */
  destroy(): void
}

RouterConfig

Configuration options for creating a router instance.

interface RouterConfig {
  /**
   * Router mode.
   */
  mode?: 'history' | 'hash' | 'memory'
  /**
   * Base path.
   */
  basePath?: string
  /**
   * Initial routes.
   */
  routes?: RouteDefinition[]
}

Types

RouteParams

URL path parameter key-value map extracted from dynamic route segments (e.g. { id: '123' }).

type RouteParams = Record<string, string>

QueryParams

URL query string parameter map (single values or arrays for repeated keys).

type QueryParams = Record<string, string | string[] | undefined>

GuardResult

Navigation guard result.

type GuardResult =
  | boolean
  | string
  | {
      path: string
      replace?: boolean
    }
  | void

NavigationGuard

Navigation guard function invoked before each navigation. Return false to cancel, a string/path to redirect, or void to allow.

type NavigationGuard = (
  to: RouteLocation,
  from: RouteLocation | null,
) => GuardResult | Promise<GuardResult>
  • to — The target route location being navigated to.
  • from — The current route location being navigated away from, or null on initial load.

RouteChangeListener

Callback invoked on each route change with the new location and the navigation action that triggered it.

type RouteChangeListener = (location: RouteLocation, action: 'push' | 'replace' | 'pop') => void

Functions

MoleculeRouterProvider(props)

Provider component that creates a molecule Router from React Router hooks and bonds it as the active singleton via @molecule/app-routing's setRouter.

Bonding happens in an effect on mount (and again whenever the location/params change), so @molecule/app-routing's navigate()/getRouter() drive THIS real React Router adapter — no manual setRouter wiring required. Mount it once inside <BrowserRouter>.

function MoleculeRouterProvider(props: MoleculeRouterProviderProps): React.JSX.Element
  • props — The provider props (see {@link MoleculeRouterProviderProps}): children (rendered inside the router context), routes (optional named-route definitions), and onRouterReady (optional callback invoked with the router after it is bonded, e.g. to register guards — bonding via setRouter happens regardless).

Returns: The rendered provider wrapping children with the router context.

useMoleculeRouter()

Hook to get the molecule Router.

function useMoleculeRouter(): Router

Returns: The molecule Router instance

useIsActive(path, exact)

Hook to check if a path is currently active.

function useIsActive(path: string, exact?: boolean): boolean
  • path — Path to check
  • exact — Whether to require exact match

Returns: Whether the path is active

useMoleculeNavigate()

Hook to get a navigate function with molecule Router options.

function useMoleculeNavigate(): Router['navigate']

Returns: Navigate function

useMoleculeQuery()

Hook to get current query params as an object.

function useMoleculeQuery(): QueryParams

Returns: Query params object

createReactRouter(config)

Creates a React Router adapter that implements the molecule Router interface.

function createReactRouter(config?: ReactRouterConfig): Router
  • config — Configuration with React Router's navigate function, location, params, and optional routes.

Returns: A molecule Router with navigation, guards, query/hash management, and route matching.

useListRoute(options)

Own URL-backed pagination, filters and selected views without mirrored state or navigation effects. Works inside BrowserRouter, MemoryRouter and data routers; no data-router-only useNavigation call or MoleculeRouterProvider is needed. Ordinary links, direct visits, and Back/Forward all read the same URL. Page links retain path parameters and unrelated queries; changing filters or views resets the page atomically. Protected views wait for auth and redirect only a confirmed signed-out session, with state.returnTo set.

function useListRoute<Filter extends string = string, Selection extends string = string>(
  options?: ListRouteOptions<Filter, Selection>,
): ListRouteResult<Filter, Selection>
  • options — App-owned filter sources, view paths, pagination and optional auth readiness.

Returns: Reactive list state plus link builders and navigation actions. Gate data queries with canLoad.

parseSearchParams(searchParams)

Parses URLSearchParams into a molecule QueryParams object. Duplicate keys are collected into arrays.

function parseSearchParams(searchParams: URLSearchParams): QueryParams
  • searchParams — The URLSearchParams instance (e.g. from useSearchParams()).

Returns: A QueryParams map where duplicate keys become string arrays.

stringifyQuery(params)

Converts a molecule QueryParams object to a URL search string (e.g. ?key=val&arr=1&arr=2). Omits keys with undefined values. Returns empty string if no params.

function stringifyQuery(params: QueryParams): string
  • params — The query parameters to stringify.

Returns: A URL search string starting with ?, or empty string if no params.

matchPath(pattern, pathname, exact)

Matches a path pattern (with :param segments and * wildcards) against a pathname.

function matchPath<Params extends RouteParams = RouteParams>(
  pattern: string,
  pathname: string,
  exact?: boolean,
): RouteMatch<Params> | null
  • pattern — The route pattern (e.g. '/users/:id').
  • pathname — The actual URL pathname to test.
  • exact — Whether to require an exact match (default true). Set to false for prefix matching.

Returns: A RouteMatch with extracted params, or null if no match.

generatePath(pattern, params)

Generates a concrete path from a route pattern by replacing :param segments with values. Throws if a required param is missing.

function generatePath(pattern: string, params?: RouteParams): string
  • pattern — The route pattern (e.g. '/users/:id').
  • params — A map of param names to values.

Returns: The resolved path string with params URL-encoded.

resolvePath(to, fromPathname)

Resolves a relative path against a base pathname. Absolute paths (starting with /) are returned as-is. Relative paths handle .. (parent) and . (current) segments.

function resolvePath(to: string, fromPathname: string): string
  • to — The destination path (absolute or relative).
  • fromPathname — The current pathname to resolve against.

Returns: The resolved absolute path.

normalizePath(path)

Removes trailing slashes from a path. Returns '/' for root/empty paths.

function normalizePath(path: string): string
  • path — The URL path to normalize.

Returns: The path without trailing slashes.

Constants

provider

Default React Router provider (basic, no hooks). For full functionality, use createReactRouter with useNavigate/useLocation/useParams.

const provider: Router

Core Interface

Implements @molecule/app-routing interface.

Bond Wiring

Setup function to register this provider with the core interface:

import { setRouter } from '@molecule/app-routing'
import { provider } from '@molecule/app-routing-react-router'

export function setupRoutingReactRouter(): void {
  setRouter(provider)
}

Injection Notes

Requirements

Peer dependencies:

  • @molecule/app-i18n ^1.0.1
  • @molecule/app-routing ^1.0.1
  • react ^18.0.0 || ^19.0.0
  • react-router ^7.0.0 || ^8.0.0

Runtime Dependencies

  • @molecule/app-i18n

  • @molecule/app-routing

  • react

  • react-router

  • useListRoute derives state from the URL on every render, including Back/Forward. Do not mirror selection/page in local state or rewrite an explicit public view when auth changes. Auth redirects replace the protected history entry only after a confirmed unauthenticated status; loading and unavailable remain in place.

  • Pass null to useQuery when list.canLoad is false or while the session is still restoring (status === 'loading'). Public views stay ready; protected views do not fall back to a public request while auth is unresolved.

  • A selection's query owns only its configured keys. undefined means absent; unrelated query values and the hash survive pagination and selection changes. Filter/selection changes reset page one atomically, unless page is explicit.

  • useListRoute does not call React Router's useNavigation: that hook needs a data router and throws under BrowserRouter. Use the data query's isFetching for list loading. No UI library, CSS class map, or data cache is required by the hook.

  • MoleculeRouterProvider bonds the router for you. On mount it calls @molecule/app-routing's setRouter with the live adapter, so other molecule packages' navigate()/getRouter() drive React Router (real SPA navigation) — no manual wiring. Pass onRouterReady only if you also need the router instance (e.g. to register guards); bonding happens either way.

  • Do NOT wire the exported provider const in a React Router app — it is a no-hooks fallback (empty params, window.location.href navigation). Use MoleculeRouterProvider (or createReactRouter with hook values) instead.

  • The provider must live INSIDE <BrowserRouter> (it calls React Router hooks).

  • Import from 'react-router', never 'react-router-dom'. The -dom package was discontinued after v7 (it was only a re-export shim); every API (BrowserRouter, Routes, Route, Link, hooks) lives in react-router.