Cookbook Router
Practical Patterns

Modular route files

Compose route declarations across files without losing parent validation or generated contracts.

Declare leaves near the feature

features/users/users.route.ts
import { defineRoute } from '@cookbook/router';

export const usersRoute = defineRoute({
  id: 'users',
  path: '/users',
});

export const userDetailsRoute = defineRoute({
  id: 'users.details',
  parent: 'users',
  path: ':userId(number)',
});

Compose once

routes.ts
import { defineRouteTree } from '@cookbook/router';
import { usersRoute, userDetailsRoute } from './features/users/users.route';

export const routes = defineRouteTree([
  usersRoute,
  userDetailsRoute,
]);

defineRouteTree() resolves parent, detects missing parents and cycles, rejects absolute child paths, and validates the composed tree. A declaration's order controls sibling order during composition.

Let the CLI discover files

cookbook-router.config.ts
import { defineRouterConfig } from '@cookbook/router-cli';

export default defineRouterConfig({
  routeFiles: ['src/**/*.route.{ts,tsx}'],
});

The generated routes.ts is conditional: it exists only when the extractor can compose route exports. Contracts and manifest generation do not require that runtime module.

Where this bites

A child declared inline under one route cannot also name a different parent. That is two ownership models for one node, and the validator rejects it.

On this page