` | `R` |
+
+## Common Pitfalls
+
+### 1. Using Async Actions
+
+Sync router doesn't work with async actions:
+
+```ts
+// Wrong - will not work as expected
+const routes = [
+ {
+ path: '/data',
+ async action() {
+ return await fetchData() // Returns a Promise, not data
+ },
+ },
+]
+
+const router = new UniversalRouterSync(routes)
+const result = router.resolve('/data') // result is a Promise!
+
+// Correct - use pre-fetched or cached data
+const routes = [
+ {
+ path: '/data',
+ action() {
+ return getCachedData() // Returns data directly
+ },
+ },
+]
+```
+
+### 2. Mixing Sync and Async Routes
+
+If some routes need async, use the async router:
+
+```ts
+// If ANY route needs async, use async router
+const routes = [
+ { path: '/', action: () => 'Home' }, // sync
+ {
+ path: '/data',
+ async action() {
+ // async
+ return await fetchData()
+ },
+ },
+]
+
+// Must use async router
+import UniversalRouter from 'universal-router'
+const router = new UniversalRouter(routes)
+```
+
+### 3. Wrong Import Path
+
+Make sure to import from the correct path:
+
+```ts
+// Wrong - this is the async router
+import UniversalRouterSync from 'universal-router'
+
+// Correct
+import UniversalRouterSync from 'universal-router/sync'
+```
+
+### 4. Expecting Promises
+
+Don't wrap sync results in Promise handling:
+
+```ts
+const router = new UniversalRouterSync(routes)
+
+// Wrong - unnecessary Promise handling
+router.resolve('/').then((result) => {
+ console.log(result)
+})
+
+// Correct - direct value
+const result = router.resolve('/')
+console.log(result)
+```
+
+## See Also
+
+- [API Reference](./api.md) - Complete API documentation
+- [TypeScript Guide](./typescript.md) - Type-safe routing
+- [Getting Started](./getting-started.md) - Basic setup
+- [Nested Routes](./nested-routes.md) - Route hierarchies
diff --git a/docs/typescript.md b/docs/typescript.md
new file mode 100644
index 0000000..fd1e98f
--- /dev/null
+++ b/docs/typescript.md
@@ -0,0 +1,649 @@
+# TypeScript Integration and Type-Safe Routes
+
+Universal Router provides comprehensive TypeScript support with automatic parameter inference,
+type-safe URL generation, and customizable context types. This guide covers everything from
+basic setup to advanced type patterns.
+
+## Quick Start
+
+Install Universal Router with TypeScript:
+
+```bash
+npm install universal-router
+```
+
+Basic typed usage:
+
+```ts
+import UniversalRouter from 'universal-router'
+
+const routes = [
+ { path: '/', action: () => 'Home' },
+ { path: '/users/:id', action: (ctx) => `User ${ctx.params.id}` },
+] as const
+
+const router = new UniversalRouter(routes)
+const result = await router.resolve('/users/123') // result is string
+```
+
+## ExtractParams - Automatic Parameter Type Inference
+
+The `ExtractParams` utility type extracts parameter types directly from path strings:
+
+```ts
+import type { ExtractParams } from 'universal-router'
+
+// Basic parameter extraction
+type UserParams = ExtractParams<'/users/:id'>
+// { id: string }
+
+type PostParams = ExtractParams<'/users/:userId/posts/:postId'>
+// { userId: string; postId: string }
+
+// Wildcard parameters become string arrays
+type FileParams = ExtractParams<'/files/*path'>
+// { path: string[] }
+
+// Combined parameters
+type ComplexParams = ExtractParams<'/orgs/:orgId/repos/*repoPath'>
+// { orgId: string; repoPath: string[] }
+
+// No parameters
+type HomeParams = ExtractParams<'/'>
+// {}
+```
+
+### Using ExtractParams in Functions
+
+```ts
+import type { ExtractParams } from 'universal-router'
+
+function fetchResource(
+ path: P,
+ params: ExtractParams
,
+): Promise {
+ let url = path as string
+ for (const [key, value] of Object.entries(params)) {
+ url = url.replace(`:${key}`, String(value))
+ }
+ return fetch(url)
+}
+
+// TypeScript enforces correct params
+fetchResource('/users/:id', { id: '123' }) // OK
+fetchResource('/users/:id', {}) // Error: missing 'id'
+fetchResource('/users/:id', { id: '1', extra: 'x' }) // Error: extra property
+```
+
+## defineRoute - Type-Safe Route Definitions
+
+The `defineRoute` helper provides type inference for route actions without manual type annotations:
+
+```ts
+import UniversalRouter, { defineRoute } from 'universal-router'
+
+// Parameters are automatically typed from the path
+const route = defineRoute({
+ path: '/users/:userId',
+ action: (context, params) => {
+ // params.userId is typed as string
+ return fetchUser(params.userId)
+ },
+})
+
+// Also works with destructured context
+const routeDestructured = defineRoute({
+ path: '/posts/:postId/comments/:commentId',
+ action: ({ params }) => {
+ // params.postId and params.commentId are both string
+ return fetchComment(params.postId, params.commentId)
+ },
+})
+```
+
+### Factory Pattern for Consistent Types
+
+Use the factory form when you need consistent result and context types across routes:
+
+```tsx
+import UniversalRouter, { defineRoute, RouterContext } from 'universal-router'
+
+// Custom context with app-specific properties
+interface AppContext extends RouterContext {
+ user?: { id: string; role: string }
+ db: Database
+}
+
+// Create a factory with fixed result and context types
+const route = defineRoute()
+
+// All routes created with this factory share the same types
+const homeRoute = route({
+ path: '/',
+ action: (context) => {
+ // context.user and context.db are available and typed
+ return
+ },
+})
+
+const userRoute = route({
+ path: '/users/:id',
+ action: (context, params) => {
+ // params.id is string, context.db is Database
+ return
+ },
+})
+```
+
+### Nested Routes with Parameter Inheritance
+
+Child routes automatically inherit parent parameters:
+
+```ts
+import { defineRoute } from 'universal-router'
+
+const routes = defineRoute({
+ path: '/orgs/:orgId',
+ children: [
+ defineRoute({
+ path: '/teams/:teamId',
+ children: [
+ defineRoute({
+ path: '/members/:memberId',
+ action: (context, params) => {
+ // All three parameters are typed:
+ // params.orgId: string
+ // params.teamId: string
+ // params.memberId: string
+ return fetchMember(params.orgId, params.teamId, params.memberId)
+ },
+ }),
+ ],
+ }),
+ ],
+})
+```
+
+## Typed Router Context
+
+Extend `RouterContext` to add custom properties available in all route actions:
+
+```ts
+import UniversalRouter, { RouterContext, Route } from 'universal-router'
+
+// Define custom context
+interface AppContext extends RouterContext {
+ user: { id: string; name: string } | null
+ locale: string
+ theme: 'light' | 'dark'
+}
+
+// Define result type
+type RouteResult = {
+ component: React.ComponentType
+ title: string
+}
+
+// Create typed routes
+const routes: Route[] = [
+ {
+ path: '/',
+ action: (context) => {
+ // context.user, context.locale, context.theme are typed
+ return {
+ component: HomePage,
+ title: context.locale === 'en' ? 'Home' : 'Accueil',
+ }
+ },
+ },
+ {
+ path: '/profile',
+ action: (context) => {
+ if (!context.user) {
+ throw { status: 401, message: 'Unauthorized' }
+ }
+ return {
+ component: () => ,
+ title: `${context.user.name}'s Profile`,
+ }
+ },
+ },
+]
+
+// Create router with typed context
+const router = new UniversalRouter(routes, {
+ context: {
+ user: null,
+ locale: 'en',
+ theme: 'light',
+ },
+})
+
+// Resolve with additional context
+const result = await router.resolve({
+ pathname: '/profile',
+ user: { id: '123', name: 'Alice' },
+})
+```
+
+## Type-Safe URL Generation
+
+The `generateUrls` function provides full type safety when routes are defined with `as const`:
+
+```ts
+import UniversalRouter from 'universal-router'
+import generateUrls from 'universal-router/generate-urls'
+
+// IMPORTANT: Use 'as const' to preserve literal types
+const routes = [
+ { name: 'home', path: '/' },
+ { name: 'users', path: '/users' },
+ { name: 'user', path: '/users/:id' },
+ { name: 'userPosts', path: '/users/:userId/posts/:postId' },
+] as const
+
+const router = new UniversalRouter(routes)
+const url = generateUrls(router)
+
+// Route names are type-checked
+url('home') // OK: '/'
+url('users') // OK: '/users'
+url('user', { id: '123' }) // OK: '/users/123'
+url('unknown') // Error: invalid route name
+
+// Required parameters are enforced
+url('user') // Error: missing params
+url('user', {}) // Error: missing 'id'
+url('userPosts', { userId: '1' }) // Error: missing 'postId'
+url('userPosts', { userId: '1', postId: '2' }) // OK
+```
+
+### ExtractRouteNames and RouteNameToParams
+
+These utility types let you work with route names and their parameters:
+
+```ts
+import type {
+ ExtractRouteNames,
+ RouteNameToParams,
+} from 'universal-router/generate-urls'
+
+const routes = [
+ { name: 'home', path: '/' },
+ { name: 'user', path: '/users/:id' },
+] as const
+
+// Extract all valid route names
+type RouteNames = ExtractRouteNames
+// 'home' | 'user'
+
+// Get params for a specific route
+type UserParams = RouteNameToParams
+// { id: string }
+
+type HomeParams = RouteNameToParams
+// {}
+```
+
+### Hierarchical Route Names
+
+With `uniqueRouteNameSep`, nested route names are also type-checked:
+
+```ts
+const routes = [
+ {
+ name: 'admin',
+ path: '/admin',
+ children: [
+ { name: 'dashboard', path: '' },
+ { name: 'users', path: '/users' },
+ { name: 'user', path: '/users/:userId' },
+ ],
+ },
+] as const
+
+const router = new UniversalRouter(routes)
+const url = generateUrls(router, { uniqueRouteNameSep: '.' })
+
+// Hierarchical names are type-checked
+url('admin') // OK: '/admin'
+url('admin.dashboard') // OK: '/admin'
+url('admin.users') // OK: '/admin/users'
+url('admin.user', { userId: '1' }) // OK: '/admin/users/1'
+url('admin.unknown') // Error: invalid route name
+```
+
+## TypedRouteContext and TypedRoute
+
+For advanced scenarios, use the typed route interfaces directly:
+
+```ts
+import type { TypedRouteContext, RouteParams } from 'universal-router'
+
+// TypedRouteContext with specific params
+interface UserContext extends TypedRouteContext<{ id: string }> {}
+
+function userAction(context: UserContext): string {
+ return `User ${context.params.id}` // params.id is string
+}
+
+// Or use inline
+const route = {
+ path: '/users/:id',
+ action: (context: TypedRouteContext<{ id: string }>) => {
+ return `User ${context.params.id}`
+ },
+}
+```
+
+## Error Handler Typing
+
+Type your error handler to match your route result type:
+
+```ts
+import UniversalRouter, { RouteError, ResolveContext } from 'universal-router'
+
+interface AppResult {
+ component: React.ComponentType
+ status: number
+}
+
+const router = new UniversalRouter(routes, {
+ errorHandler(error: RouteError, context: ResolveContext): AppResult {
+ if (error.status === 404) {
+ return {
+ component: NotFoundPage,
+ status: 404,
+ }
+ }
+ return {
+ component: () => ,
+ status: error.status || 500,
+ }
+ },
+})
+```
+
+## Custom resolveRoute Typing
+
+Type-safe custom route resolution:
+
+```ts
+import UniversalRouter, {
+ RouteContext,
+ RouteParams,
+ RouteResult,
+} from 'universal-router'
+
+interface PageResult {
+ content: string
+ meta: { title: string }
+}
+
+// Custom route with additional properties
+interface AppRoute {
+ path: string
+ page?: string
+ data?: (params: RouteParams) => Promise
+}
+
+const router = new UniversalRouter(routes, {
+ async resolveRoute(
+ context: RouteContext,
+ params: RouteParams,
+ ): Promise {
+ const route = context.route as AppRoute
+
+ if (route.page) {
+ const module = await import(route.page)
+ const data = route.data ? await route.data(params) : null
+ return module.default(params, data)
+ }
+
+ return undefined
+ },
+})
+```
+
+## Strict Mode Configuration
+
+Enable strict TypeScript configuration for best type safety:
+
+```json
+{
+ "compilerOptions": {
+ "strict": true,
+ "noImplicitAny": true,
+ "strictNullChecks": true,
+ "noUncheckedIndexedAccess": true
+ }
+}
+```
+
+## Generic Route Patterns
+
+Create reusable typed route patterns:
+
+```ts
+import { defineRoute, RouterContext } from 'universal-router'
+
+// Generic CRUD route factory
+function createCrudRoutes(
+ resource: string,
+ handlers: {
+ list: () => Promise
+ get: (id: string) => Promise
+ create: (data: Partial) => Promise
+ update: (id: string, data: Partial) => Promise
+ delete: (id: string) => Promise
+ },
+) {
+ const route = defineRoute()
+
+ return [
+ route({
+ path: `/${resource}`,
+ action: () => handlers.list(),
+ }),
+ route({
+ path: `/${resource}/:id`,
+ action: (ctx, params) => handlers.get(params.id),
+ }),
+ // ... more routes
+ ]
+}
+
+// Usage
+interface User {
+ id: string
+ name: string
+ email: string
+}
+
+const userRoutes = createCrudRoutes('users', {
+ list: () => fetchUsers(),
+ get: (id) => fetchUser(id),
+ create: (data) => createUser(data),
+ update: (id, data) => updateUser(id, data),
+ delete: (id) => deleteUser(id),
+})
+```
+
+## Middleware with Typed Context
+
+Create type-safe middleware that extends context:
+
+```ts
+import UniversalRouter, { RouteContext, RouterContext } from 'universal-router'
+
+interface BaseContext extends RouterContext {
+ requestId: string
+}
+
+interface AuthContext extends BaseContext {
+ user: { id: string; role: string }
+}
+
+// Middleware that adds auth context
+async function withAuth(
+ context: RouteContext,
+ next: () => Promise,
+): Promise {
+ const user = await authenticateRequest(context.requestId)
+ if (!user) {
+ throw { status: 401, message: 'Unauthorized' }
+ }
+
+ // Extend context with user
+ ;(context as RouteContext).user = user
+ return next()
+}
+
+// Usage in route
+const protectedRoute = {
+ path: '/admin',
+ async action(context: RouteContext) {
+ // Apply middleware
+ return withAuth(context, async () => {
+ // context.user is now available
+ if (context.user.role !== 'admin') {
+ throw { status: 403, message: 'Forbidden' }
+ }
+ return 'Admin Dashboard'
+ })
+ },
+}
+```
+
+## Common TypeScript Patterns
+
+### Discriminated Union Results
+
+```ts
+type RouteResult =
+ | { type: 'page'; component: React.ComponentType; title: string }
+ | { type: 'redirect'; url: string; permanent: boolean }
+ | { type: 'error'; status: number; message: string }
+
+const routes = [
+ {
+ path: '/',
+ action: (): RouteResult => ({
+ type: 'page',
+ component: HomePage,
+ title: 'Home',
+ }),
+ },
+ {
+ path: '/old-page',
+ action: (): RouteResult => ({
+ type: 'redirect',
+ url: '/new-page',
+ permanent: true,
+ }),
+ },
+]
+
+// Handle results with exhaustive checking
+async function handleRoute(pathname: string) {
+ const result = await router.resolve(pathname)
+
+ switch (result.type) {
+ case 'page':
+ document.title = result.title
+ render(result.component)
+ break
+ case 'redirect':
+ if (result.permanent) {
+ // 301 redirect
+ }
+ navigate(result.url)
+ break
+ case 'error':
+ showError(result.status, result.message)
+ break
+ default:
+ // TypeScript ensures all cases are handled
+ const _exhaustive: never = result
+ }
+}
+```
+
+### Conditional Types for Optional Params
+
+```ts
+import type { ExtractParams, Prettify } from 'universal-router'
+
+// Make specific params optional
+type WithOptional = Prettify<
+ Omit & Partial>
+>
+
+// Usage
+type Params = ExtractParams<'/users/:id/posts/:postId'>
+// { id: string; postId: string }
+
+type ParamsWithOptionalPost = WithOptional
+// { id: string; postId?: string }
+```
+
+## Common Pitfalls
+
+### 1. Forgetting `as const`
+
+Without `as const`, literal types are widened:
+
+```ts
+// Wrong - types widened to string
+const routes = [{ name: 'home', path: '/' }]
+// typeof routes[0].name is string
+
+// Correct - literal types preserved
+const routes = [{ name: 'home', path: '/' }] as const
+// typeof routes[0].name is 'home'
+```
+
+### 2. Incorrect Parameter Access
+
+Always use the params object, not direct access:
+
+```ts
+// Wrong - params not accessible this way
+{
+ path: '/users/:id',
+ action: (context) => context.id // Error: id doesn't exist on context
+}
+
+// Correct - use params object
+{
+ path: '/users/:id',
+ action: (context) => context.params.id // OK
+}
+
+// Also correct - destructure params
+{
+ path: '/users/:id',
+ action: ({ params }) => params.id // OK
+}
+```
+
+### 3. Missing Return Type Annotation
+
+Let TypeScript infer types or annotate explicitly:
+
+```ts
+// Avoid any - be explicit about result types
+const router = new UniversalRouter(routes) // Result is any
+
+// Better - specify result type
+const router = new UniversalRouter(routes)
+const router = new UniversalRouter(routes)
+const router = new UniversalRouter(routes)
+```
+
+## See Also
+
+- [URL Generation](./url-generation.md) - Type-safe URL generation
+- [Nested Routes](./nested-routes.md) - Parameter inheritance in nested routes
+- [Authorization](./authorization.md) - Typed auth context patterns
+- [Universal Router API](./api.md) - Complete API reference
diff --git a/docs/url-generation.md b/docs/url-generation.md
new file mode 100644
index 0000000..05ba5f2
--- /dev/null
+++ b/docs/url-generation.md
@@ -0,0 +1,528 @@
+# Generating URLs from Route Names
+
+Universal Router provides a `generateUrls` function that creates URL paths from route names and
+parameters. This approach prevents hardcoded URLs scattered throughout your codebase and ensures
+URLs stay in sync with your route definitions.
+
+## Why Use Named Routes?
+
+Hardcoded URLs create maintenance problems:
+
+```js
+// Fragile - if route changes, must update everywhere
+;View Profile
+navigate('/users/123/profile')
+redirect('/users/123/profile')
+```
+
+Named routes centralize URL structure:
+
+```js
+// Robust - route definition is the single source of truth
+;View Profile
+navigate(url('userProfile', { id: '123' }))
+redirect(url('userProfile', { id: '123' }))
+```
+
+## Basic Usage
+
+Import `generateUrls` from the dedicated module and create a URL generator from your router:
+
+```js
+import UniversalRouter from 'universal-router'
+import generateUrls from 'universal-router/generate-urls'
+
+// Use 'as const' to preserve literal types for type-safe route names
+const routes = [
+ { name: 'home', path: '/' },
+ { name: 'users', path: '/users' },
+ { name: 'user', path: '/users/:id' },
+ { name: 'userPosts', path: '/users/:userId/posts/:postId' },
+] as const
+
+const router = new UniversalRouter(routes)
+const url = generateUrls(router)
+
+url('home') // '/'
+url('users') // '/users'
+url('user', { id: '123' }) // '/users/123'
+url('userPosts', { userId: '123', postId: '456' }) // '/users/123/posts/456'
+```
+
+> **Why `as const`?** Without it, TypeScript widens route names to `string`, losing
+> type safety. With `as const`, you get autocomplete for route names and compile-time
+> errors for typos. See [TypeScript Integration](./typescript.md) for details.
+
+## Route Naming Conventions
+
+Choose clear, consistent names for your routes:
+
+```js
+const routes = [
+ // Resource-based naming
+ { name: 'users', path: '/users' }, // List
+ { name: 'user', path: '/users/:id' }, // Show
+ { name: 'userEdit', path: '/users/:id/edit' }, // Edit
+ { name: 'userNew', path: '/users/new' }, // Create form
+
+ // Feature-based naming
+ { name: 'dashboard', path: '/dashboard' },
+ { name: 'settings', path: '/settings' },
+ { name: 'settingsProfile', path: '/settings/profile' },
+ { name: 'settingsSecurity', path: '/settings/security' },
+]
+```
+
+## Nested Routes and Hierarchical Names
+
+For nested routes, you can use the `uniqueRouteNameSep` option to create hierarchical names
+automatically:
+
+```js
+const routes = [
+ {
+ name: 'admin',
+ path: '/admin',
+ children: [
+ { name: 'dashboard', path: '' },
+ { name: 'users', path: '/users' },
+ {
+ name: 'settings',
+ path: '/settings',
+ children: [
+ { name: 'general', path: '' },
+ { name: 'security', path: '/security' },
+ ],
+ },
+ ],
+ },
+]
+
+const router = new UniversalRouter(routes)
+
+// Without separator - names are independent (may conflict)
+const url = generateUrls(router)
+url('dashboard') // '/admin'
+url('users') // '/admin/users'
+url('general') // '/admin/settings'
+
+// With separator - names are prefixed with parent names
+const urlWithSep = generateUrls(router, { uniqueRouteNameSep: '.' })
+urlWithSep('admin') // '/admin'
+urlWithSep('admin.dashboard') // '/admin'
+urlWithSep('admin.users') // '/admin/users'
+urlWithSep('admin.settings') // '/admin/settings'
+urlWithSep('admin.settings.general') // '/admin/settings'
+urlWithSep('admin.settings.security') // '/admin/settings/security'
+```
+
+### Choosing a Separator
+
+Common separators include:
+
+- `.` (dot): `admin.users.edit` - Most common, familiar from object notation
+- `/` (slash): `admin/users/edit` - Mirrors URL structure
+- `:` (colon): `admin:users:edit` - Clear visual separation
+
+```js
+// Dot notation (recommended)
+generateUrls(router, { uniqueRouteNameSep: '.' })
+
+// Slash notation
+generateUrls(router, { uniqueRouteNameSep: '/' })
+
+// Colon notation
+generateUrls(router, { uniqueRouteNameSep: ':' })
+```
+
+## Query String Parameters
+
+Parameters not used in the path can become query strings using the `stringifyQueryParams` option:
+
+```js
+import generateUrls from 'universal-router/generate-urls'
+
+const routes = [
+ { name: 'search', path: '/search' },
+ { name: 'users', path: '/users' },
+ { name: 'user', path: '/users/:id' },
+]
+
+const router = new UniversalRouter(routes)
+
+// Custom query string serializer
+const url = generateUrls(router, {
+ stringifyQueryParams(params) {
+ return new URLSearchParams(params).toString()
+ },
+})
+
+// Path params used in URL, extra params become query string
+url('search', { q: 'javascript', page: '1' })
+// '/search?q=javascript&page=1'
+
+url('user', { id: '123', tab: 'posts', sort: 'date' })
+// '/users/123?tab=posts&sort=date'
+
+url('users', { role: 'admin', status: 'active' })
+// '/users?role=admin&status=active'
+```
+
+### Using qs Library for Complex Query Strings
+
+For nested objects and arrays in query strings, use a library like `qs`:
+
+```js
+import qs from 'qs'
+import generateUrls from 'universal-router/generate-urls'
+
+const url = generateUrls(router, {
+ stringifyQueryParams(params) {
+ const query = qs.stringify(params, { arrayFormat: 'brackets' })
+ return query ? `?${query}` : ''
+ },
+})
+
+url('search', {
+ filters: { category: 'tech', price: { min: 10, max: 100 } },
+ tags: ['javascript', 'typescript'],
+})
+// '/search?filters[category]=tech&filters[price][min]=10&filters[price][max]=100&tags[]=javascript&tags[]=typescript'
+```
+
+## Type-Safe URL Generation
+
+When using TypeScript with `as const` route definitions, `generateUrls` provides full type safety:
+
+```ts
+import UniversalRouter from 'universal-router'
+import generateUrls from 'universal-router/generate-urls'
+
+// Use 'as const' for type inference
+const routes = [
+ { name: 'home', path: '/' },
+ { name: 'user', path: '/users/:id' },
+ { name: 'post', path: '/posts/:postId/comments/:commentId' },
+] as const
+
+const router = new UniversalRouter(routes)
+const url = generateUrls(router)
+
+// TypeScript knows valid route names
+url('home') // OK
+url('user', { id: '1' }) // OK
+url('invalid') // Error: Argument of type '"invalid"' is not assignable
+
+// TypeScript enforces required parameters
+url('user') // Error: Expected 2 arguments, but got 1
+url('user', {}) // Error: Property 'id' is missing
+url('user', { id: '1', extra: 'ok' }) // OK - extra params allowed
+```
+
+### Type-Safe Hierarchical Names
+
+With the separator option, hierarchical names are also type-checked:
+
+```ts
+const routes = [
+ {
+ name: 'admin',
+ path: '/admin',
+ children: [
+ { name: 'users', path: '/users' },
+ { name: 'user', path: '/users/:userId' },
+ ],
+ },
+] as const
+
+const router = new UniversalRouter(routes)
+const url = generateUrls(router, { uniqueRouteNameSep: '.' })
+
+url('admin') // OK: '/admin'
+url('admin.users') // OK: '/admin/users'
+url('admin.user', { userId: '1' }) // OK: '/admin/users/1'
+
+url('admin.invalid') // Error: not a valid route name
+url('admin.user') // Error: missing required userId param
+```
+
+## Base URL Support
+
+If your router has a `baseUrl`, generated URLs include it automatically:
+
+```js
+const router = new UniversalRouter(routes, {
+ baseUrl: '/app',
+})
+
+const url = generateUrls(router)
+
+url('home') // '/app/'
+url('user', { id: '1' }) // '/app/users/1'
+```
+
+## Path Encoding Options
+
+Control how parameters are encoded using path-to-regexp options:
+
+```js
+const url = generateUrls(router, {
+ // Custom encoder (default is encodeURIComponent)
+ encode: (value, token) => {
+ // Don't encode slashes for wildcard params
+ if (token.name === 'path') {
+ return value
+ }
+ return encodeURIComponent(value)
+ },
+})
+
+// With wildcard route: { name: 'file', path: '/files/*path' }
+url('file', { path: 'docs/api/readme.md' })
+// '/files/docs/api/readme.md' (slashes preserved)
+```
+
+## Integration Patterns
+
+### React Navigation Helper
+
+Create a custom hook for type-safe navigation:
+
+```tsx
+import { useMemo, useCallback } from 'react'
+import UniversalRouter from 'universal-router'
+import generateUrls from 'universal-router/generate-urls'
+
+const routes = [
+ { name: 'home', path: '/' },
+ { name: 'user', path: '/users/:id' },
+ { name: 'userPosts', path: '/users/:userId/posts' },
+] as const
+
+const router = new UniversalRouter(routes)
+
+export function useNavigation() {
+ const url = useMemo(() => generateUrls(router), [])
+
+ const navigate = useCallback(
+ [0]>(
+ name: Name,
+ ...args: Parameters extends [Name, ...infer Rest]
+ ? Rest
+ : never
+ ) => {
+ const path = url(name, ...args)
+ window.history.pushState(null, '', path)
+ // Trigger your router's resolve
+ },
+ [url],
+ )
+
+ return { url, navigate }
+}
+
+// Usage in components
+function UserLink({ userId }: { userId: string }) {
+ const { url } = useNavigation()
+
+ return View Profile
+}
+```
+
+### Express/Server Integration
+
+Generate URLs for server-side redirects and links:
+
+```ts
+import express from 'express'
+import UniversalRouter from 'universal-router'
+import generateUrls from 'universal-router/generate-urls'
+
+const routes = [
+ { name: 'home', path: '/' },
+ { name: 'login', path: '/login' },
+ { name: 'user', path: '/users/:id' },
+] as const
+
+const router = new UniversalRouter(routes)
+const url = generateUrls(router)
+
+const app = express()
+
+// Redirect using named routes
+app.get('/old-profile/:id', (req, res) => {
+ res.redirect(301, url('user', { id: req.params.id }))
+})
+
+// Generate URLs in templates
+app.get('/dashboard', (req, res) => {
+ res.render('dashboard', {
+ links: {
+ home: url('home'),
+ profile: url('user', { id: req.user.id }),
+ },
+ })
+})
+```
+
+### Link Component
+
+Create a reusable Link component with named route support:
+
+```tsx
+import { AnchorHTMLAttributes } from 'react'
+import generateUrls from 'universal-router/generate-urls'
+
+// Assuming routes and router are defined elsewhere
+import { router, routes } from './routes'
+
+const url = generateUrls(router)
+
+type RouteName = Parameters[0]
+
+interface LinkProps
+ extends Omit, 'href'> {
+ to: Name
+ params?: Parameters[1]
+}
+
+function Link({
+ to,
+ params,
+ children,
+ onClick,
+ ...props
+}: LinkProps) {
+ const href = url(to, params)
+
+ function handleClick(e: React.MouseEvent) {
+ if (onClick) onClick(e)
+ if (e.defaultPrevented) return
+ if (e.metaKey || e.ctrlKey || e.shiftKey) return
+
+ e.preventDefault()
+ window.history.pushState(null, '', href)
+ // Trigger navigation
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+
+// Usage
+ View User
+ Home
+```
+
+## Error Handling
+
+The `generateUrls` function throws errors for invalid route names:
+
+```js
+const url = generateUrls(router)
+
+try {
+ url('nonexistent')
+} catch (error) {
+ console.error(error.message) // 'Route "nonexistent" not found'
+}
+
+try {
+ url('user') // Missing required 'id' param
+} catch (error) {
+ console.error(error.message) // Error from path-to-regexp about missing param
+}
+```
+
+### Safe URL Generation
+
+Create a wrapper that returns `null` for invalid routes:
+
+```ts
+function safeUrl(name: string, params?: Record): string | null {
+ try {
+ return url(name, params)
+ } catch {
+ console.warn(`Failed to generate URL for route "${name}"`)
+ return null
+ }
+}
+
+// Usage
+const href = safeUrl('user', { id: '123' }) ?? '/fallback'
+```
+
+## Common Pitfalls
+
+### 1. Forgetting `as const`
+
+Without `as const`, TypeScript cannot infer literal types:
+
+```ts
+// Wrong - types are widened to string
+const routes = [{ name: 'home', path: '/' }]
+// name is string, not 'home'
+
+// Correct - literal types preserved
+const routes = [{ name: 'home', path: '/' }] as const
+// name is 'home'
+```
+
+### 2. Route Name Conflicts
+
+Without a separator, nested routes can have conflicting names:
+
+```js
+const routes = [
+ {
+ name: 'users',
+ path: '/admin/users',
+ children: [
+ { name: 'users', path: '/public/users' }, // Conflict!
+ ],
+ },
+]
+
+const url = generateUrls(router)
+url('users') // Which one? Throws error: Route "users" already exists
+```
+
+Solution: Use `uniqueRouteNameSep` or unique names.
+
+### 3. Missing Parameters
+
+All path parameters must be provided:
+
+```js
+const routes = [{ name: 'post', path: '/users/:userId/posts/:postId' }]
+
+// Wrong - missing postId
+url('post', { userId: '1' }) // Throws error
+
+// Correct - all params provided
+url('post', { userId: '1', postId: '2' })
+```
+
+### 4. Array Parameters for Wildcards
+
+Wildcard parameters expect arrays:
+
+```js
+const routes = [{ name: 'files', path: '/files/*path' }]
+
+// For single segment
+url('files', { path: ['readme.md'] }) // '/files/readme.md'
+
+// For multiple segments
+url('files', { path: ['docs', 'api', 'readme.md'] }) // '/files/docs/api/readme.md'
+```
+
+## See Also
+
+- [TypeScript Integration](./typescript.md) - Full TypeScript setup with type inference
+- [Nested Routes](./nested-routes.md) - Working with nested route structures
+- [SPA Navigation](./spa-navigation.md) - Client-side navigation patterns
+- [Universal Router API](./api.md) - Complete API reference