@molecule/app-routing-next
Provider bond · routing · App (browser) · v1.0.2 · Apache-2.0
Next.js App Router provider for molecule.dev
npm install @molecule/app-routing-nextnpm · 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
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.
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.tsJSDoc, not this file.
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
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[]
}
NextSearchParams
Next Search Params interface.
interface NextSearchParams {
[key: string]: string | string[] | undefined
}
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[]
}
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>
}
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>
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, ornullon 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
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, optionalroutes) assembled fromnext/navigationhooks.
Returns: The bonded molecule Router.
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 amatchpattern (supports*wildcards) and an asynccheckfunction.
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'snavigation(fromuseRouter()),pathname,searchParams,params, and optionalroutes.
Returns: A molecule Router with navigation, guards, query/hash management, and route matching.
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.
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.
Constants
provider
Default Next.js router provider (basic, no hooks). For full functionality, use
createNextRouter with useRouter/usePathname/useSearchParams/useParams.
const provider: Router
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
search— The query string to parse (with or without leading?).
Returns: A key-value map of query parameters.
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
params— The query parameters to serialize.
Returns: The query string (e.g. ?foo=bar&baz=1) or ''.
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
pattern— Route pattern with:paramplaceholders.pathname— The actual URL pathname to match against.exact— Iftrue, requires a full match (no trailing segments).
Returns: A RouteMatch with extracted params, or null if no match.
generatePath
Generates a URL path from a route pattern by substituting named parameters.
const generatePath: (pattern: string, params?: RouteParams) => string
pattern— Route pattern with:paramplaceholders (e.g./users/:id).params— Parameter values to substitute into the pattern.
Returns: The generated path with parameters URL-encoded.
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.1react^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'ssetRouterin an effect, so molecule packages'navigate()/getRouter()drive the real App Router. -
Do NOT wire the exported
providerconst in a Next app. It is a no-hooks fallback: its location is frozen at import time,getParams()is always empty, andnavigate()falls back towindow.location.href— a full page reload that bypasses the App Router. -
Forgotten wiring never errors.
@molecule/app-routing'sgetRouter()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()(formiddleware.ts) returns plain{ redirect: string } | { continue: true }objects — map them toNextResponse.redirect(new URL(result.redirect, request.url))/NextResponse.next()yourself; returning the guard result directly does nothing. -
searchParamsmust be a plain object — spread the hook value withObject.fromEntries(useSearchParams()).