URL to application intent

white-label-router

Use one route contract across browser and server runtimes while preserving real links, URL semantics, History API behavior, and requestable server routes.

Install and requirements

  • Node.js ^22.18.0 or >=24.11.0
  • Browser History API/location/DOM events only for browser enhancement
  • Directly requestable server routes for public browser URLs
import Router from 'white-label-router';
const router = new Router();

Route definitions and matching

Routes are evaluated in insertion order and match complete path boundaries. /products matches /products and /products/42 but not /products-old. Put specific prefixes before broad prefixes.

A route can be a function or an object with title, focus, secure, and view lifecycle fields. secure() must return exactly true to allow navigation. defaultRoute runs when no configured prefix matches.

router.routes = {
    '/products/sale': saleRoute,
    '/products': productsRoute,
    defaultRoute
};

Progressive enhancement

Browser navigation should start with a real anchor. data-pushstate opts eligible same-origin links into client-side navigation. Modified clicks, non-left clicks, downloads, alternate targets, and cross-origin URLs stay native.

Initialization dispatches the current URL without creating a duplicate history entry. Back/forward navigation reads the authoritative window.location rather than trusting arbitrary history state.

<a href="/products/42" data-pushstate>View product 42</a>

Location payload

Route callbacks receive scope and a location object. data.url contains decoded path segments after the matched prefix, data.query contains the last value for each query key, and data.mediator contains programmatic/mediator navigation metadata.

Applications remain responsible for validating decoded URL values before using them as trusted domain data.

{
  url: '/products/42?color=blue',
  data: {
    url: ['42'],
    query: {color: 'blue'},
    mediator: {source: 'featured'}
  }
}

Route lifecycle, title, and focus

On a successful route change, the previous route view's destroy() runs before the next view's initialize(). Browser object routes can update document title and focus after rendering.

The default focus selector is main h1. Set focus: false when navigation represents an in-page refinement that should preserve focus. Server runtimes keep pageTitle state but do not mutate a document or focus target.

Configuration properties

router.routes

Ordered route table containing route functions or lifecycle route objects.

Returns
Configuration property.
Runtime
Browser + Node.js

router.scope

Application Element passed to callbacks; null is valid on server paths.

Returns
Configuration property.
Runtime
Browser + Node.js

router.mediator

Optional EventEmitter-compatible source for router:navigate messages.

Returns
Configuration property.
Runtime
Browser + Node.js

route.title

Document title applied after browser routing; retained as pageTitle state on servers.

Returns
Route configuration.
Runtime
Browser + Node.js

route.focus

Selector to focus after browser navigation; defaults to main h1, false preserves focus.

Returns
Route configuration.
Runtime
Browser

route.secure(scope, location)

Navigation guard. Only an exact true result allows the route.

Returns
unknown; navigation accepts only true.
Runtime
Browser + Node.js

route.view

Route function or lifecycle object with initialize() and optional destroy().

Returns
Route configuration.
Runtime
Browser + Node.js

Router API

new Router()

Create an independent router with empty routes and isolated listener state.

Returns
A Router instance.
Runtime
Browser + Node.js

initialize(url?)

Dispatch the current browser URL or an explicit server URL and attach applicable listeners.

ParameterTypeDefaultDescription
urlstringOptional in browsers; server applications should normally provide the request URL.
Returns
The same Router instance.
Runtime
Browser + Node.js

addListeners()

Attach browser and optional mediator handlers once.

Returns
The same Router instance.
Runtime
Browser + Node.js
  • Repeated initialization is idempotent.

destroy()

Release routing listeners owned by the instance.

Returns
The same Router instance after cleanup.
Runtime
Browser + Node.js

removeListeners()

Release listeners owned by this router; subclasses may extend this lifecycle hook.

Returns
The same Router instance.
Runtime
Browser + Node.js

eventPushStateClick(event)

Intercept an eligible same-origin data-pushstate link while preserving native browser actions that should not be enhanced.

Returns
Router instance after handled navigation; true when native/default handling should continue.
Runtime
Browser

eventPopState()

Dispatch the authoritative current browser history location without adding a new entry.

Returns
The same Router instance.
Runtime
Browser

parseQueryString(query)

Decode query parameters using URLSearchParams and keep the last duplicate value.

Returns
Plain Record<string, string>.
Runtime
Browser + Node.js

setLocationData(data?)

Rebuild decoded path/query/mediator payload for the selected route.

Returns
undefined; updates locationData in place.
Runtime
Browser + Node.js

applyPageContext(route)

Apply configured title and focus after routing when the runtime supports those effects.

Returns
The same Router instance.
Runtime
Browser + Node.js

TypeScript

Router.Route, Router.Location, Router.Navigation, and Router.Handler expose the supported public route contracts.

DOM types are present because the same class supports browser scopes/events; server hosts do not need DOM globals at runtime.

Security and trust boundaries

URL and query values are decoded, not trusted. Validate them before using them for authorization, data access, or domain decisions.

route.secure is an application guard hook; Router does not implement authentication policy.

Design boundaries

Router owns URL-to-intent translation. It does not own state, rendering, data loading, authentication policy, or server infrastructure.

Real links and directly requestable server URLs remain the foundation; Router enhances them instead of replacing them.