← All @molecule/* packages · App templates

@molecule/app-routing-next

Provider bond · routing · App (browser) · v1.0.1 · Apache-2.0

Next.js App Router provider for molecule.dev

npm install @molecule/app-routing-next

npm · Source on GitHub · Implements @molecule/app-routing

How it works

@molecule/app-routing-next is a provider bond on the app (browser) side: it implements the routing core interface (@molecule/app-routing) with a concrete vendor or library behind it.

Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.

'use client'

import { useParams, usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useMoleculeRouter } from '@molecule/app-routing-next'

// Mount once near the root layout. useMoleculeRouter recreates + re-bonds on every
// route change (via setRouter in an effect) so location/params stay current and
// @molecule/app-routing's navigate() drives THIS App Router.
export function MoleculeRouterBridge({ children }: { children: React.ReactNode }) {
  useMoleculeRouter({
    navigation: useRouter(),
    pathname: usePathname(),
    searchParams: Object.fromEntries(useSearchParams()),
    params: useParams(),
  })

  return children
}

Works with: @molecule/app-i18n, @molecule/app-logger, @molecule/app-routing

Reference

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.

Next.js App Router provider for molecule.dev.

Adapts Next.js App Router navigation to the molecule Router interface from @molecule/app-routing, so molecule packages and app code can navigate, read params/query, and register guards without importing next/navigation.

Quick Start

'use client'

import { useParams, usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useMoleculeRouter } from '@molecule/app-routing-next'

// Mount once near the root layout. useMoleculeRouter recreates + re-bonds on every
// route change (via setRouter in an effect) so location/params stay current and
// @molecule/app-routing's navigate() drives THIS App Router.
export function MoleculeRouterBridge({ children }: { children: React.ReactNode }) {
  useMoleculeRouter({
    navigation: useRouter(),
    pathname: usePathname(),
    searchParams: Object.fromEntries(useSearchParams()),
    params: useParams(),
  })

  return children
}

Type

provider

Installation

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

API

Interfaces

MiddlewareGuardRule

Middleware guard rule configuration.

interface MiddlewareGuardRule {
  /**
   * Path pattern to match (supports * wildcards).
   */
  match: string

  /**
   * Check function - return true to allow, string to redirect.
   */
  check: (request: {
    url: string
    pathname: string
    cookies: { get: (name: string) => { value: string } | undefined }
    headers: { get: (name: string) => string | null }
  }) => boolean | string | Promise<boolean | string>
}

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
}

NextNavigation

Next.js navigation types (minimal subset to avoid direct next dependency).

interface NextNavigation {
  push(href: string, options?: { scroll?: boolean }): void
  replace(href: string, options?: { scroll?: boolean }): void
  back(): void
  forward(): void
  refresh(): void
  prefetch?(href: string): void
}

NextParams

Next Params interface.

interface NextParams {
  [key: string]: string | string[]
}

NextRouterConfig

Configuration options for the Next.js router provider.

interface NextRouterConfig extends RouterConfig {
  /**
   * Next.js navigation object (from useRouter).
   */
  navigation?: NextNavigation

  /**
   * Current pathname (from usePathname).
   */
  pathname?: string

  /**
   * Current search params (from useSearchParams).
   */
  searchParams?: NextSearchParams

  /**
   * Current dynamic params (from useParams).
   */
  params?: NextParams

  /**
   * Route definitions.
   */
  routes?: RouteDefinition[]
}

NextSearchParams

Next Search Params interface.

interface NextSearchParams {
  [key: string]: string | string[] | undefined
}

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[]
}

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
}

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

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>

QueryParams

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

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

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

RouteParams

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

type RouteParams = Record<string, string>

Functions

createLinkHref(pathname, query, hash)

Creates a Next.js link href with query params.

function createLinkHref(pathname: string, query?: QueryParams, hash?: string): string
  • pathname — The base path (e.g. '/products').
  • query — Optional query parameters to append as a search string.
  • hash — Optional hash fragment (with or without leading #).

Returns: The assembled href string with path, query, and hash.

createMiddlewareGuard(rules)

Next.js middleware helper for route guards.

function createMiddlewareGuard(
  rules: MiddlewareGuardRule[],
): (request: {
  url: string
  nextUrl: { pathname: string }
  cookies: { get: (name: string) => { value: string } | undefined }
  headers: { get: (name: string) => string | null }
}) => Promise<
  { redirect: string; continue?: undefined } | { continue: boolean; redirect?: undefined }
>
  • rules — Array of guard rules, each with a match pattern (supports * wildcards) and an async check function.

Returns: An async middleware function that returns { redirect: string } or { continue: true }.

createNextRouter(config)

Creates a Next.js App Router adapter.

function createNextRouter(config?: NextRouterConfig): Router
  • config — Configuration with Next.js's navigation (from useRouter()), pathname, searchParams, params, and optional routes.

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

dynamicPath(pattern)

Creates a reusable path builder for Next.js dynamic routes. Replaces [param] and [...catchAll] segments with provided values.

function dynamicPath(pattern: string): (params: Record<string, string | string[]>) => string
  • pattern — A Next.js route pattern with [param] or [...catchAll] segments.

Returns: A function that accepts params and returns the resolved path string.

parseCatchAllParams(param)

Normalizes a Next.js catch-all route parameter into a string array. Handles undefined (returns []), a single string, or an existing array.

function parseCatchAllParams(param: string | string[] | undefined): string[]
  • param — The catch-all param value from Next.js route params.

Returns: An array of path segments.

useMoleculeRouter(config)

Builds the molecule Router from next/navigation hook values AND bonds it via @molecule/app-routing's setRouter, so navigate()/getRouter() drive the REAL App Router (not the core's auto-created fallback that does full-page reloads).

Call it in a 'use client' component near the root layout, passing the values from useRouter()/usePathname()/useSearchParams()/useParams(). It rebuilds and re-bonds on every route change so location/params stay current.

function useMoleculeRouter(config: NextRouterConfig): Router
  • config — The Next.js router config (navigation, pathname, searchParams, params, optional routes) assembled from next/navigation hooks.

Returns: The bonded molecule Router.

Constants

generatePath

Generates a URL path from a route pattern by substituting named parameters.

const generatePath: (pattern: string, params?: RouteParams) => string

matchPath

Matches a route pattern (e.g. /users/:id) against a pathname. Extracts named parameters from the URL.

const matchPath: <Params extends RouteParams = RouteParams>(
  pattern: string,
  pathname: string,
  exact?: boolean,
) => RouteMatch<Params> | null

parseQuery

Parses a URL query string (e.g. ?foo=bar&baz=1) into a QueryParams object. Duplicate keys produce string arrays.

const parseQuery: (search: string) => QueryParams

provider

Default Next.js router provider (basic, no hooks). For full functionality, use createNextRouter with useRouter/usePathname/useSearchParams/useParams.

const provider: Router

stringifyQuery

Serializes a QueryParams object into a query string with leading ?. Returns an empty string if no parameters are present.

const stringifyQuery: (params: QueryParams) => string

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-next'

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

Injection Notes

Requirements

Peer dependencies:

  • @molecule/app-i18n ^1.0.1
  • @molecule/app-logger ^1.0.1
  • @molecule/app-routing ^1.0.1
  • react ^18.0.0 || ^19.0.0

Runtime Dependencies

  • @molecule/app-i18n

  • @molecule/app-logger

  • @molecule/app-routing

  • react

  • Use useMoleculeRouter(...) to bond the router from a 'use client' component near the root layout (the adapter cannot run in Server Components). It calls @molecule/app-routing's setRouter in an effect, so molecule packages' navigate()/getRouter() drive the real App Router.

  • Do NOT wire the exported provider const in a Next app. It is a no-hooks fallback: its location is frozen at import time, getParams() is always empty, and navigate() falls back to window.location.href — a full page reload that bypasses the App Router.

  • Forgotten wiring never errors. @molecule/app-routing's getRouter() auto-creates a plain browser router when nothing is bonded, so molecule packages keep "working" with full-page reloads — check wiring first when SPA navigation degrades.

  • createMiddlewareGuard() (for middleware.ts) returns plain { redirect: string } | { continue: true } objects — map them to NextResponse.redirect(new URL(result.redirect, request.url)) / NextResponse.next() yourself; returning the guard result directly does nothing.

  • searchParams must be a plain object — spread the hook value with Object.fromEntries(useSearchParams()).