T8 React Router

Type safety

Type-safe routing is as an optional enhancement. It's enabled by supporting route patterns created with a type-safe URL builder like url-shape together with a schema created with zod or yup. This approach allows for gradual or partial adoption of type-safe routing in an application.

import { A, useRoute } from "@t8/react-router";
import { createURLSchema } from "url-shape";
import { z } from "zod";

const { url } = createURLSchema({
  "/": null, // Goes without parameters
  "/sections/:id": {
    // Path components
    params: z.object({
      id: z.coerce.number(),
    }),
    // Similarly a `query` schema can be added here
  },
});

let App = () => {
  let { withRoute } = useRoute();

  return (
    <>
      <header className={withRoute(url("/"), "full", "compact")}>
        <h1>App</h1>
        <nav>
          <A href={url("/")}>Intro</A>{" | "}
          <A href={url("/sections/:id", { params: { id: 1 } })}>Start</A>
        </nav>
      </header>
      {withRoute(url("/"), <Intro/>)}
      {withRoute(url("/sections/:id"), ({ params }) => (
        <Section id={params.id}/>
      ))}
    </>
  );
};

Type-safe routing live demo

🔹 The url() function is a type-safe URL builder. It creates a URL with a URL pattern defined in the schema and typed parameters that are prevalidated against the given schema: typos and type mismatches are highlighted in a type-aware code editor. See url-shape for more details.

🔹 A URL schema doesn't have to cover the entire app. Standalone portions of an app can have their own URL schemas.

🔹 Optionally, application-wide type safety can be achieved by disallowing URLs and URL patterns other than provided by the URL builder (the url() function in the example above):

declare module "@t8/react-router" {
  interface Config {
    strict: true;
  }
}

Adding this type declaration to an app effectively disallows using string and RegExp values for routes and route patterns (such as in the route link href prop, route.assign(location), and the routing function withRoute(routePattern, x, y)), only allowing values returned from the URL builder with the same routing APIs.

🔹 A URL builder pattern (like url("/sections/:id")) can also be used with useRouteState(pattern) and useRouteMatch(pattern) to manipulate URL parameters in a type-safe manner.

Typed URL parameters state demo

🔹 Recap: It's using a typed URL pattern (like with url() from url-shape) that enables type-safe route handling, which is an optional enhancement. Plain string routes and RegExp route patterns are handled with more generic typing as a baseline sufficient in many cases.