paramour
A routing companion your compiler approves of.
Validated route and search params, typed path building, and an explicit URL wire format for the Next.js App Router — with the Standard Schema validator you already use.
import { , , } from "paramour";
export const = ("/product/[id]", {
: { : .() },
: { : .().() },
});
// typed, validated, explicit: "/product/42?q=paramour"
const link = (, { : { : 42 }, : { : "paramour" } });
// a string into p.integer() fails to compile:
href(, { : { : "42" } });- Standard Schema: zod, valibot, arktype
- Zero runtime dependencies
- Published wire-format spec
- ESM-only, tree-shakeable
- MIT
The problem
The URL is an API. Most apps type it by hand.
Template literals compile no matter what they say, and everything read back from a URL is a string until proven otherwise.
declare const : { : number };
declare const : <string, string | undefined>;
// nothing is watching a template literal — the typo ships
const = `/produtc/${.}?page=2`;
// every read is a guess
const = (.); // NaN when absentimport { , , } from "paramour";
export const = ("/product/[id]", {
: { : .() },
: { : .().(1) },
});
// the path is real, the types are checked, absence has a rule
const = (, {
: { : 42 },
: { : 2 },
});Both of these compile. Only one of them is checked.
Features
Why paramour
Small pieces that agree with each other — and with your compiler.
Bring your own validator
Validate with zod, valibot, arktype — any Standard Schema library. Paramour owns the wire format; your validator owns the rules.
Explicit wire format
Every codec serializes and parses by a published, numbered spec. URLs are predictable enough to hold us to them.
Route objects as currency
Routes are imported objects, not string registries. Unused routes tree-shake away; the compiler sees every reference.
CLI workflows
generate, check, init, list, doctor — a registry codegen and drift guard that runs in CI (and builds this site).
Teaches your AI agent
An Agent Skills package ships in the box — paramour skills gives Claude Code, Cursor, and Codex version-accurate instructions, not guesswork.
Devtools panel
A TanStack Devtools panel showing wire vs parsed values, codec shapes, and decode issues live as you navigate.
nuqs adapter
Derive nuqs parsers from a route's search codecs — one definition for links, hooks, and client URL state.
Ecosystem
One route definition, the whole toolchain
Everything downstream — hooks, codegen, devtools, nuqs parsers, lint rules — reads the same route object.
Codecs, route definitions, href, and the reflection API — the core that carries the spec.
withTypedRoutes, App and Pages Router hooks, a testing provider, and the paramour CLI.
TanStack Devtools panel: wire vs parsed values and decode issues, live.
nuqs parsers derived from a route's search codecs.
no-raw-hrefs — every link goes through href().
Comparison
How it compares
Against next-typesafe-url and Next.js's built-in typedRoutes.
| Feature | paramour | next-typesafe-url | typedRoutes |
|---|---|---|---|
| Typed path building | ✓ | ✓ | ✓ |
| Route params validated at runtime | ✓ | ✓ | — |
| Search params validated at runtime | ✓ | ✓ | — |
| Validator | any Standard Schema | zod | — |
| Library-owned serialization with a published spec | ✓ | — | — |
| Hooks for reading params | ✓ | ✓ | — |
| CI drift check (check, doctor) | ✓ | — | — |
| Bundled agent skill for coding agents | ✓ | — | — |
| Devtools panel | ✓ | — | — |
| nuqs adapter | ✓ | — | — |
| Testing provider (no next/* mocks) | ✓ | — | — |
| ESLint rule for raw hrefs | ✓ | — | — |
As of next-typesafe-url 5.x and the typedRoutes option in Next.js 16. Spotted something out of date? Open an issue.
Coming from next-typesafe-url?
The architecture barely moves — what changes is the vocabulary. Your route definitions stay colocated, your hooks keep their names, and $path({ route, ... }) becomes href(route, ...). The migration guide was written by migrating a real app, route by route.
Start with one route
pnpm add paramour @paramour-js/nextGet started