public-rules › TanStack Router techs/tanstack-router

Register the router and read route data through typed route APIs

MEDIUM1.0.0
When to apply Before planning, writing, changing, or reviewing TanStack Router setup or components that read params, search params, loader data, or context, or that navigate.

Register the router type once with the Register interface, and read params, search params, and loader data through route-specific APIs, such as Route.useParams(), getRouteApi('/path'), or hooks with from.

Implementation

  • Declare Register next to where the router is created: declare module '@tanstack/react-router' { interface Register { router: ReturnType<typeof getRouter> } }.
  • In a route file, use the route's own hooks, such as Route.useParams() and Route.useLoaderData().
  • In a component split from its route, use getRouteApi('/posts/$postId').
  • Pass from to generic hooks, such as useParams({ from: '/posts/$postId' }), when neither of the above is convenient.
  • Use strict: false only in components shared across routes, such as breadcrumbs, and handle every field as possibly missing.
  • Navigate with Link or navigate using route paths and params, not hand-built URL strings, so TypeScript checks them.

Rationale

TanStack Router infers route paths, params, search params, and loader data from the route tree, but global hooks and Link see those types only after the router is registered. Route-specific APIs return exact types for one route, while strict: false returns a loose union where every field may be missing.

Examples

Application: Registration

Incorrect (counterexample):

export const router = createRouter({ routeTree });

Without registration, Link and navigate accept any to value, so a mistyped route compiles.

Correct:

export const router = createRouter({ routeTree });

declare module '@tanstack/react-router' {
  interface Register {
    router: typeof router;
  }
}

Application: Reading params in a route component

Incorrect (counterexample):

function PostDetail() {
  const params = useParams({ strict: false });
  return <Post id={params.postId!} />;
}

postId is typed as possibly missing, so the component needs a non-null assertion that hides real mistakes.

Correct:

const postRoute = getRouteApi('/posts/$postId');

function PostDetail() {
  const { postId } = postRoute.useParams();
  return <Post id={postId} />;
}

Validation

Run the type checker and check that a navigation to a nonexistent route or with missing params fails to compile. Search route-specific components for strict: false and non-null assertions on params.

strict: false in a component genuinely shared across routes is not a violation.