Repository navigation
Replies: 2 comments 1 reply
|
I've been searching for an answer to this problem for hours. We use the exact same pattern with central routes object which is really useful for things like refactoring etc. The code you've linked is actually really neat and almost a complete solution. If only One other semi-solution I was thinking about was not restricting the exported route object name to "Route", but allowing some regex pattern - e.g. having "Route" as a suffix. This would allow us to export semantically named variables from the route files themselves: import { createFileRoute } from '@tanstack/react-router'
function RouteComponent() {
return <div>Hello !</div>
}
export const DashboardRoute = createFileRoute('/_mainLayout/dashboard')({
component: RouteComponent,
})It wouldn't completely solve the original problem, but at least it would allow me to know what route to import at a glance via intellisense, instead of scrolling through 50+ "Route" object suggestions from different destinations. EDIT: I'm editing this so that other people do not get too optimistic. Sooner or later you are gonna run into problems with "path" vs "to" values - particularly when dealing with index routes. Since the discussed |
|
Here is a slightly modified approach: import type { FileRouteTypes } from "#/routeTree.gen"
// 1. Get all valid paths from the router (but instead via routeTree.gen)
type AppPath = keyof FileRouteTypes["fileRoutesByTo"]
// 2. Define your constants
export const ROUTES = {
HOME: "/",
SEARCH: "/search", // /search is a folder with an index.tsx file in it. The approach above forces this path to have a trailing slash.
LOGIN: "/login",
LOGOUT: "/logout",
} as const satisfies Record<string, AppPath>
// 3. Utility types to check coverage
type RoutesValues = (typeof ROUTES)[keyof typeof ROUTES]
type MissingRoutes = Exclude<AppPath, RoutesValues>
// 4. The Exhaustiveness Check
// If 'MissingRoutes' is not never, this type resolves to an error object,
// causing a build failure at the const below.
type AssertAllRoutesAreDefined = MissingRoutes extends never
? true
: {
error: `Some routes are missing in ROUTES constant: ${MissingRoutes}`
}
// This will error if you add a new file but forget to add it to ROUTES
export const _exhaustiveCheck: AssertAllRoutesAreDefined = true |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I've been using TanStack Router in a large-scale project. While I love the type safety of the generated strings, my team prefers using a centralized ROUTES constant object (e.g., ROUTES.LOGIN) rather than repeating string literals throughout the app.
The challenge with a manual constants file is that it usually isn't type-safe—if you rename a file or add a new route, the constants file drifts from the actual router definition.
I implemented a TypeScript solution that utilizes RegisteredRouter["routesByPath"] to create an exhaustive check. It throws a compile-time error if my ROUTES constant is missing any route defined in the router.
I thought this might be useful for others looking for a "Named Routes" pattern, or perhaps spark a discussion on if a similar mapping could be auto-generated.
Why this is useful:
Allows usage of ROUTES.NAME intellisense throughout the app.
Prevents the constants file from becoming stale.
Leverages the existing type generation without needing a secondary codegen step.
Has anyone else implemented a similar pattern, or is there a recommended way to handle "Named Routes" aliases in TanStack Router?
All reactions