Skip to main content
Factory function to create type-safe path builders that work with auto-generated route type definitions. This is designed to work with the vite-plugin’s generateTypedRoutes option.

Signature

function createTypedPaths<
  TRoutes extends Record<string, Record<string, string>>
>(): TypedPaths<TRoutes>

Parameters

TRoutes
Record<string, Record<string, string>>
required
Type parameter representing the mapping of route paths to their parameter types.This is typically auto-generated by the vite-plugin as RouteParams.

Return Type

TypedPaths
TypedPaths<TRoutes>
An object with a for() method for building type-safe paths.
for
function
Build a URL path with type-checked parameters.Parameters:
  • path: string - Route path template (can include ?query suffix)
  • params?: Record<string, string> - Path parameters (required if route has dynamic segments)
Returns: Resolved URL path string

Examples

Basic Setup

// routes.generated.ts (auto-generated by vite-plugin)
import { createTypedPaths } from "@tailor-platform/app-shell";

type RouteParams = {
  "/": {};
  "/dashboard": {};
  "/orders/:id": { id: string };
  "/orders/:orderId/items/:itemId": { orderId: string; itemId: string };
};

export const paths = createTypedPaths<RouteParams>();

Static Routes

import { paths } from "./routes.generated";

// Static route - no params needed
paths.for("/dashboard"); // → "/dashboard"
paths.for("/"); // → "/"

Dynamic Routes

import { paths } from "./routes.generated";

// Dynamic route - params required and type-checked
paths.for("/orders/:id", { id: "123" }); // → "/orders/123"

// Multiple dynamic segments
paths.for("/orders/:orderId/items/:itemId", { 
  orderId: "456", 
  itemId: "789" 
}); // → "/orders/456/items/789"

Routes with Query Strings

import { paths } from "./routes.generated";

// Static route with query string
paths.for("/dashboard?tab=overview"); // → "/dashboard?tab=overview"

// Dynamic route with query string
paths.for("/orders/:id?tab=details", { id: "123" }); 
// → "/orders/123?tab=details"

paths.for("/items/:id?tab=details&view=compact", { id: "456" }); 
// → "/items/456?tab=details&view=compact"

URL Encoding

import { paths } from "./routes.generated";

// Dynamic segments are automatically URL-encoded
paths.for("/products/:id", { id: "foo bar" }); 
// → "/products/foo%20bar"

paths.for("/products/:id", { id: "special/chars?#" }); 
// → "/products/special%2Fchars%3F%23"

Catch-all Routes

// Type definition with catch-all segment
type RouteParams = {
  "/docs/*path": { path: string };
};

const paths = createTypedPaths<RouteParams>();

// Catch-all segments preserve slashes, encode individual segments
paths.for("/docs/*path", { path: "api/reference/hooks" });
// → "/docs/api/reference/hooks"

paths.for("/docs/*path", { path: "guide/getting started" });
// → "/docs/guide/getting%20started"

Type Safety

import { paths } from "./routes.generated";

// ✅ Correct usage
paths.for("/dashboard"); 
paths.for("/orders/:id", { id: "123" });

// ❌ TypeScript errors
paths.for("/orders/:id"); 
// Error: params required

paths.for("/orders/:id", { orderId: "123" }); 
// Error: wrong param name (should be 'id')

paths.for("/invalid"); 
// Error: path not found in RouteParams

Usage in Navigation

import { Link } from "react-router";
import { paths } from "./routes.generated";

const OrdersList = ({ orders }: { orders: Order[] }) => {
  return (
    <div>
      {orders.map((order) => (
        <Link 
          key={order.id} 
          to={paths.for("/orders/:id", { id: order.id })}
        >
          Order {order.id}
        </Link>
      ))}
    </div>
  );
};

Programmatic Navigation

import { useNavigate } from "react-router";
import { paths } from "./routes.generated";

const ProductCard = ({ productId }: { productId: string }) => {
  const navigate = useNavigate();
  
  const handleClick = () => {
    navigate(paths.for("/products/:id", { id: productId }));
  };
  
  return <button onClick={handleClick}>View Product</button>;
};

Building URLs from Data

import { paths } from "./routes.generated";

const buildOrderUrl = (orderId: string, itemId?: string) => {
  if (itemId) {
    return paths.for("/orders/:orderId/items/:itemId", { 
      orderId, 
      itemId 
    });
  }
  return paths.for("/orders/:id", { id: orderId });
};

// Usage
buildOrderUrl("123"); // → "/orders/123"
buildOrderUrl("123", "456"); // → "/orders/123/items/456"

TypeScript Types

// Extract base path (before ?) from a path with query string
type ExtractBasePath<T extends string> = T extends `${infer Base}?${string}`
  ? Base
  : T;

// Check if an object type has no keys
type IsEmptyObject<T> = keyof T extends never ? true : false;

// Type-safe path builder interface
type TypedPaths<TRoutes extends Record<string, Record<string, string>>> = {
  for<
    T extends (keyof TRoutes & string) | `${keyof TRoutes & string}?${string}`,
  >(
    path: T,
    ...params: ExtractBasePath<T> extends keyof TRoutes
      ? IsEmptyObject<TRoutes[ExtractBasePath<T>]> extends true
        ? []
        : [TRoutes[ExtractBasePath<T>]]
      : never
  ): string;
};

Implementation Details

Path Parameter Replacement

The for() method replaces dynamic segments in the path template:
  1. Dynamic segments (:paramName): Replaced with the encoded parameter value
  2. Catch-all segments (*paramName): Replaced with the parameter value, encoding each path segment individually while preserving slashes

URL Encoding

  • Dynamic segments (:param) are encoded using encodeURIComponent(), which encodes the entire value including slashes
  • Catch-all segments (*param) split the value on /, encode each segment individually, then rejoin with / to preserve path structure

Notes

  • This function is designed to work with auto-generated route types from the vite-plugin
  • Path parameters are automatically URL-encoded
  • Query strings can be included in the path template using ? syntax
  • TypeScript will enforce that all required parameters are provided with correct types
  • Empty parameter objects ({}) indicate routes with no dynamic segments

Build docs developers (and LLMs) love