API.md

@lazarv/react-server/router

Typed routing primitives for the file-system and programmatic routers. Includes route factories, schema-based validators, and the type-level helpers that extract params from route patterns.

function createRoute<TPath extends string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>>(path: TPath, element: React.ReactNode, options: RouteOptions<TPath, TParams, TSearch> & { parse: { search: { [K in keyof TSearch]: (value: string) => TSearch[K]; }; }; }): TypedRoute<TPath, TParams, TSearch, Partial<TSearch>>; function createRoute<TPath extends string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>>(path: TPath, element: React.ReactNode, options?: RouteOptions<TPath, TParams, TSearch>): TypedRoute<TPath, TParams, TSearch>; function createRoute(path: "*", element: React.ReactNode, options?: Omit<RouteOptions<"*">, "exact">): TypedRoute<"*", {}, {}>; function createRoute(path: `${string}/*`, element: React.ReactNode, options?: Omit<RouteOptions<string>, "exact">): TypedRoute<string, {}, {}>; function createRoute(element: React.ReactNode, options?: Omit<RouteOptions<"*">, "exact">): TypedRoute<"*", {}, {}>; function createRoute(path: "*", options?: Omit<RouteOptions<"*">, "exact">): RouteDescriptor<"*", {}, {}>; function createRoute(path: `${string}/*`, options?: Omit<RouteOptions<string>, "exact">): RouteDescriptor<string, {}, {}>; function createRoute<TPath extends string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>>(path: TPath, options: RouteOptions<TPath, TParams, TSearch> & { parse: { search: { [K in keyof TSearch]: (value: string) => TSearch[K]; }; }; }): RouteDescriptor<TPath, TParams, TSearch, Partial<TSearch>>; function createRoute<TPath extends string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>>(path: TPath, options?: RouteOptions<TPath, TParams, TSearch>): RouteDescriptor<TPath, TParams, TSearch>; function createRoute<TPath extends string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>, TSearchInput = TSearch>(descriptor: RouteDescriptor<TPath, TParams, TSearch, TSearchInput>, element: React.ReactNode, options?: { loading?: React.ComponentType | React.ReactNode; resources?: (RouteResourceBinding | RouteResource | ClientRouteResources)[]; }): TypedRoute<TPath, TParams, TSearch, TSearchInput>;

Create a typed route with parse-based search — search params are optional on Link

function createRouter<T extends Record<string, TypedRoute<any, any, any, any>>>(routes: T): TypedRouter<T>;

Collect typed routes into a router.

const router = createRouter({ home, about, user, notFound }); <router.Routes /> <router.user.Link params={{ id: "42" }}>User</router.user.Link>
function useActionState<T extends (...args: any[]) => T extends (...args: any[]) => infer R ? R : any, E = Error>(action: T): ActionState<ReturnType<T>, E>;

This hook returns the current state of the passed server action. The state includes the form data, the data returned by the action, the error if the action failed and also the action's internal id.

Parameters

Returns — The current state of the referenced action

import { useActionState } from '@lazarv/react-server'; import { addTodo } from './actions'; export default function AddTodo() { const { formData, error } = useActionState(addTodo); return ( <form action={addTodo}> <input name="title" type="text" defaultValue={formData?.get?.("title") as string} /> <button type="submit">Submit</button> {error?.map?.(({ message }, i) => ( <p key={i}>{message}</p> )) ?? (error && ( <p>{error}</p> ))} </form> ); }
function useMatch<T = RouteParams>(path: string, options?: MatchOptions): T | null;

This hook returns the route parameters for the given path.

Parameters

Returns — The route parameters for the given path

import { useMatch } from '@lazarv/react-server'; export default function Todo() { const { id } = useMatch('/todos/[id]'); return <p>Todo id: {id}</p>; }
const Route: React.FC<React.PropsWithChildren<{ path: string; exact?: boolean; matchers?: RouteMatchers; element?: React.ReactElement; render?: (props: React.PropsWithChildren<RouteParams>) => React.ReactElement; fallback?: boolean; }>>;

Represents a route in the application.

import { Route } from '@lazarv/react-server'; export default function App() { return ( <Route path="/todos" exact> <Todos /> </Route> ); }
const SearchParams: React.FC<SearchParamsProps>;

Bidirectional search-param transform boundary.

Wrap your routes (or the entire app) to intercept how search params are read from and written to the URL on the client.

import { SearchParams } from "@lazarv/react-server/router"; <SearchParams decode={(sp) => { sp.delete("utm_source"); return sp; }} > {children} </SearchParams>
interface AssertSchema<T = any> { assert(data: unknown): T; }

Schema with ArkType-style .assert() (throws on failure, returns T).

interface ParseSchema<T = any> { parse(data: unknown): T; }

Schema with a generic .parse() that throws on failure.

interface RouteDescriptor<TPath extends string = string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>, TSearchInput = TSearch> { readonly path: TPath | undefined; readonly fallback: boolean; readonly exact: boolean; readonly validate: RouteValidate<TParams, TSearch> | null; readonly parse: RouteParse<TParams, TSearch> | null; Link: TPath extends "*" ? never : ExtractParams<TPath> extends Record<string, never> ? React.FC<Omit<import("../client/navigation").LinkProps<string>, "to" | "search"> & { to?: never; params?: never; search?: TSearchInput | ((prev: TSearch) => TSearchInput); }> : React.FC<Omit<import("../client/navigation").LinkProps<string>, "to" | "search"> & { to?: never; params: TParams; search?: TSearchInput | ((prev: TSearch) => TSearchInput); }>; href: TPath extends "*" ? never : ExtractParams<TPath> extends Record<string, never> ? (params?: never) => string : (params: TParams) => string; useParams(): TParams | null; useSearchParams(): TSearch; SearchParams: React.FC<SearchParamsProps>; }

Route descriptor returned by the client-safe createRoute (from navigation). Contains route metadata, href(), and a typed .Link component — but no .Route. Both descriptors and full TypedRoute instances satisfy this.

interface RouteOptions<TPath extends string = string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>> { exact?: boolean; loading?: React.ComponentType | React.ReactNode; matchers?: RouteMatchers; render?: (params: TParams & { children?: React.ReactNode; }) => React.ReactNode; children?: React.ReactNode; validate?: RouteValidate<TParams, TSearch>; parse?: RouteParse<TParams, TSearch>; resources?: (RouteResourceBinding | RouteResource | ClientRouteResources)[]; }
interface RouteParse<TParams = any, TSearch = any> { params?: { [K in keyof TParams]?: (value: string) => TParams[K]; }; search?: { [K in keyof TSearch]?: (value: string) => TSearch[K]; }; }

Lightweight parser map for route params and search params. Each key maps to a function that converts the raw string to the desired type. Built-in constructors like Number, Boolean, and Date work directly.

const user = createRoute("/user/[id]", { parse: { params: { id: Number } } }); // user.useParams() → { id: 42 } instead of { id: "42" }
interface RouteResource { query: (key?: any) => Promise<any>; }

A resource descriptor (singleton) that can be used directly in a route's resources array without .from().

interface RouteResourceBinding { resource: { query: (key?: any) => Promise<any>; }; mapFn: (routeParams: Record<string, any>, searchParams: Record<string, any>) => any; }

A route-resource binding — see @lazarv/react-server/resources. Returned by resource.from(mapFn).

interface RouteValidate<TParams = any, TSearch = any> { params?: ValidateSchema<TParams>; search?: ValidateSchema<TSearch>; }
interface SafeParseSchema<T = any> { parse(data: unknown): T; safeParse(data: unknown): { success: true; data: T; } | { success: false; error: unknown; }; }

Schema with Zod / Valibot-style .safeParse() + .parse().

interface SearchParamsProps { decode?: (searchParams: URLSearchParams) => URLSearchParams; encode?: (searchParams: URLSearchParams, current: URLSearchParams) => URLSearchParams; children?: React.ReactNode; }

Props for the <SearchParams> transform boundary.

Both are optional. Nesting is supported — decode chains outer→inner, encode chains inner→outer.

interface TypedRoute<TPath extends string = string, TParams = ExtractParams<TPath>, TSearch = Record<string, string>, TSearchInput = TSearch> extends RouteDescriptor<TPath, TParams, TSearch, TSearchInput> { Route: React.FC<Partial<{ element: React.ReactNode; loading: React.ComponentType | React.ReactNode; render: (params: TParams & { children?: React.ReactNode; }) => React.ReactNode; children: React.ReactNode; exact: boolean; fallback: boolean; }>>; }
type ActionState<T, E> = { formData: FormData | null; data: T | null; error: E | null; actionId: string | null; };

Represents the state of a server action.

Client resource binding(s) from a "use client" module. Opaque on the server — passes through RSC serialization and resolves on the client for navigation pre-loading. Can be a single binding or an array of bindings exported from a "use client" module.

Place alongside server bindings in the resources array:

resources: [serverBinding, clientBinding]
type ExtractParams<T extends string> = T extends `${string}[...${infer P}]${infer R}` ? { [K in P]: string[]; } & ExtractParams<R> : T extends `${string}[${infer P}]${infer R}` ? { [K in P]: string; } & ExtractParams<R> : {};

Extract typed params from a route path pattern.

ExtractParams<"/user/[id]"> // { id: string } ExtractParams<"/blog/[slug]/[commentId]"> // { slug: string; commentId: string } ExtractParams<"/files/[...path]"> // { path: string[] } ExtractParams<"/about"> // {}
type InferSchema<T> = T extends SafeParseSchema<infer U> ? U : T extends AssertSchema<infer U> ? U : T extends ParseSchema<infer U> ? U : never;

Infer the output type of a ValidateSchema

type MatchOptions = { exact?: boolean; fallback?: boolean; matchers?: RouteMatchers; };

Options for the useMatch hook.

type RouteMatchers = Record<string, (value: string) => boolean>;
type RouteParams = Record<string, string>;
type TypedRouter<T extends Record<string, TypedRoute<any, any, any, any>>> = { Routes: React.FC; SearchParams: React.FC<SearchParamsProps>; } & { [K in keyof T]: T[K]; };

Any schema that exposes at least one recognized validation method.

Supported patterns (tried in this order at runtime):

  1. .safeParse() — Zod, Valibot
  2. .assert() — ArkType
  3. .parse() — generic fallback

You can use any library whose schema objects satisfy one of these shapes.