Dev Highlights

Доки по разработке

This project is maintained by teniryte

Типобезопасность маршрутизатора

После создания router объявите модульное расширение:

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

Так Link, useNavigate, useRouterState будут знать про params, search, context.

Тонкости:

Типизация компонентов маршрутов

type RouteComponentProps<T extends AnyRoute> = {
  route: T
  useLoaderData: () => LoaderData<T>
}

export function PostPage({
  useLoaderData,
}: RouteComponentProps<typeof Route>) {
  const post = useLoaderData()
}

Лайфхаки:

Контекст и createRootRouteWithContext

interface RouterContext {
  queryClient: QueryClient
  session: Session | null
}

export const rootRoute = createRootRouteWithContext<RouterContext>()({
  beforeLoad: ({ context }) => {
    invariant(context.session, 'Auth required')
  },
})

Схемы параметров и поиска

Пример:

const searchSchema = z.object({
  page: z.coerce.number().int().positive().default(1),
  tags: z.array(z.string()).catch([]),
})

export const postsRoute = createFileRoute('/posts')({
  validateSearch: searchSchema.parse,
  loaderDeps: ({ search }) => ({ page: search.page }),
  loader: async ({ deps }) => fetchPosts(deps.page),
})

Референсы маршрутов и helpers

export const routes = router.buildRouteTree()
routes.blog.post.$postId({ postId: 1 }).to

buildRouteTree() создает объект с типизированными to, fullPath, params, useLoaderData, useStaticData.

Полезные приёмы:

Общие хуки и утилиты

export const useRouteParams = <T extends AnyRoute>(route: T) => route.useParams()

export function useTypedNavigate<TRoute extends AnyRoute>(route: TRoute) {
  const navigate = useNavigate()
  return (opts?: Parameters<typeof route.navigate>[0]) =>
    navigate({ ...opts, to: route.to })
}

Ленивые маршруты и code splitting

Навигация, prefetch и guards

ESLint и tsconfig

Модульные утилиты и DI

src/lib/router.ts:

export const router = createRouter({ routeTree, context })
export type AppRouter = typeof router
export type RoutePaths = keyof AppRouter['flatRoutes']
export type RouteIds = AppRouter['routeTree']['id']
export type RouterRoute<T extends RouteIds> = RouterRouteById<AppRouter, T>

Тестирование маршрутов

Логирование, метрики, Observability

Лучшие практики

  1. Избегайте строковых путей — используйте routes.* или RouteById.
  2. Все значения search/params локализуйте в одном helper (легче рефакторить).
  3. Проверяйте опции маршрута через satisfies RouteOptions.
  4. Выносите типы loaders: type LoaderData = Awaited<ReturnType<typeof loader>>.
  5. beforeLoad и action описывайте через satisfies, чтобы не потерять context.
  6. Передавайте select в route.useLoaderData({ select }), чтобы типы соответствовали выбранному полю.
  7. Для multi-tenant проектов используйте брендовые типы TenantId, AccountId и возвращайте их в parseParams.
  8. Создавайте expectType<RouteById<'known/id'>>() в тестах — IDE гарантирует, что ID существует.

Чек-лист