Доки по разработке
This project is maintained by teniryte
export const Route = createRoute({
getParentRoute: () => rootRoute,
path: 'projects/$projectId/tasks/$taskId',
parseParams: ({ projectId, taskId }) => ({
projectId: Number(projectId),
taskId: taskId as TaskId,
}),
stringifyParams: ({ projectId, taskId }) => ({
projectId: projectId.toString(),
taskId,
}),
})
$ для одного сегмента, $...slug (splat) — для «остального» пути./audit/$...path).path: '/') рядом с динамическими родителями дают быстрый доступ к overview (projects/$projectId/).parseParams helper и переиспользуйте через routeOptions.parseParams + beforeLoad, чтобы сразу редиректить на 404 при неверных значениях.parseParams можно вычислить производные флаги (isArchived: projectId.startsWith('arch_')) и использовать их во всех дочерних компонентах.Number() в компонентах.router.matchRoute('/projects/42'), чтобы убедиться, что params валидны ещё до рендера.const Route = createRoute({
getParentRoute: () => rootRoute,
path: 'reports',
validateSearch: z.object({
tab: z.enum(['summary', 'details']).default('summary'),
from: z.coerce.date().optional(),
to: z.coerce.date().optional(),
}),
})
const { tab, from } = Route.useSearch()
const params = Route.useParams()
useSearch возвращает типизированный объект; дефолты прописываются в схеме и работают и при SSR, и при навигации из кода.
const navigate = Route.useNavigate()
const search = Route.useSearch()
const toggleTab = () =>
navigate({
search: (prev) => ({ ...prev, tab: prev.tab === 'summary' ? 'details' : 'summary' }),
replace: true,
})
replace: true + дебаунс — лучшее решение для форм фильтрации, чтобы не засорять историю.useSearchState (hook из Router), который возвращает [value, setValue] и упрощает связь с контролами.validateSearch, и все потомки автоматически наследуют схему.parentSearchSchema.extend(...), сохраняя дефолты (page, pageSize)..transform((data) => ({ ...data, page: 1 })), либо напишите собственный mergeSearch.const Route = createRoute({
getParentRoute: () => rootRoute,
path: 'grid',
parseSearch: (search) => ({
page: Number(search.page ?? 1),
tags: (search.tags?.split(',') ?? []).filter(Boolean),
}),
stringifySearch: ({ page, tags }) => ({
page,
...(tags.length ? { tags: tags.join(',') } : {}),
}),
})
parseSearch удобен, когда часть параметров приходит не по схеме (напр., массив через tags=a,b); дополнительно можно запускать zod внутри.stringifySearch — лучшее место для удаления пустых значений: возвращайте объект без ключей, чтобы URL не разрастался.validateSearch (типизация) и stringifySearch (нормализация), если нужна и строгая проверка, и красивый URL.const url = router.buildLocation({
to: '/blog/$postId',
params: { postId: 7 },
search: { highlight: true },
hash: 'comments',
})
// → { href: '/blog/7?highlight=true#comments', pathname: '/blog/7', searchStr: '?highlight=true', ... }
router.buildLink() вернёт готовые href/onClick для кастомных ссылок (например, внутри Markdown-рендера).Используйте router.buildRouteTree() или router.routes (при автоматической генерации) для создания helpers:
routes.blog.post.$postId({ postId: 8, search: { preview: true } })
<ButtonLink route={routes.blog.post.$postId} params={{ postId: 8 }} /> сам формировал URL.routes.blog.post._ctx.loader?.preload({ postId }), чтобы пользователь мгновенно увидел данные.hash: 'comments' — добавляет #comments.state в navigate позволяет передавать произвольные данные (не сериализуются в URL).state только непродолжительные данные (fromModal: true). При обновлении страницы state исчезает, поэтому критичные значения — только в search.zod/valibot или кастомную функцию.useSearch() никогда не вернёт undefined.shouldReload/loaderDeps, чтобы чётко описать, какие параметры должны перезагружать данные.const paginationSearch = z.object({
page: z.coerce.number().int().positive().default(1),
pageSize: z.coerce.number().int().positive().max(200).default(25),
})
const filtersSearch = paginationSearch.extend({
statuses: z.array(z.enum(['open', 'closed', 'draft'])).default(['open']),
})
export const TicketsRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'tickets',
validateSearch: filtersSearch,
})
const navigate = Route.useNavigate()
const search = Route.useSearch()
const toggleColumn = (id: string) =>
navigate({
search: (prev) => ({
...prev,
columns: { ...prev.columns, [id]: !prev.columns?.[id] },
}),
replace: true,
})
z.record(z.enum(columnIds), z.boolean()).default({}).localStorage, а при монтировании вызывайте router.navigate({ to: Route.to, search: restoreFilters(), replace: true }), чтобы восстановить пользовательский контекст.router.parseLocation(window.location) показывает, как URL интерпретирован (params/search/hash).router.options.defaultPreload = 'intent', чтобы заранее валидировать params при наведении на ссылки.router.subscribe('onRouteError', (error) => …) помогает ловить ошибки валидации и отображать дружелюбные сообщения.router.__store.getState().location и проверять, что search/params сериализуются как ожидается.parse/stringify.stringifySearch удаляет пустые значения и сортирует ключи.