T
TanStack16mo ago
stormy-gold

Combining file based routes with code based routing

We're migrating a big and old codebase from react-router@v3 and exploring TanStack Router. We intend to primarily setup our routes through the recommended file-based routing, but our application has a lot of redirects from older migrations that we need to keep working. We need to target some wildcard routes, and we've found a way to achieve this in the router.tsx, but it feels a little hacky. Is there a best practice to define code based routing besides file based routing that doesn't involve mutating the auto-generated routeTree?
// app/router.tsx
import { routeTree } from './routeTree.gen'

// Capture existing children from the generated routeTree (file-based)
const fileBasedRoutes = routeTree.children || []

// `addChildren()` mutates and replaces any previousy children
routeTree.addChildren([
// Since `addChildren()` replaces the children, spread the existing children back in the new array
...fileBasedRoutes,

// Create code-based routes to add to the route tree's children
...createWildcardRedirectRoutes('aaa', 'bbb'),
...createWildcardRedirectRoutes('create/station', 'create/music'),
])

export const router = createRouter({
routeTree,
})
// app/router.tsx
import { routeTree } from './routeTree.gen'

// Capture existing children from the generated routeTree (file-based)
const fileBasedRoutes = routeTree.children || []

// `addChildren()` mutates and replaces any previousy children
routeTree.addChildren([
// Since `addChildren()` replaces the children, spread the existing children back in the new array
...fileBasedRoutes,

// Create code-based routes to add to the route tree's children
...createWildcardRedirectRoutes('aaa', 'bbb'),
...createWildcardRedirectRoutes('create/station', 'create/music'),
])

export const router = createRouter({
routeTree,
})
8 Replies
stormy-gold
stormy-goldOP16mo ago
/** Creates redirect routes */
function createWildcardRedirectRoutes(
from: string,
to: string,
parentRoute = rootRoute,
) {
let fromRegexPattern = `/(?:${from})(/|$)`
if (to[0] === '/') {
// Support absolute `to` paths via leading slash
fromRegexPattern = '^.+' + fromRegexPattern
to = to.substr(1)
}
if (to.length) {
to = '/' + to
}
const fromRegex = new RegExp(fromRegexPattern)
const beforeLoad = ({ location }) => {
const newTo = location.pathname.replace(fromRegex, `${to}$1`)
throw redirect({ replace: true, to: newTo })
}

// Optional params matching isn't supported, so we need to create a route for the direct
// match, as well as one with a route splat ($)
return [
createRoute({
path: `${from}/`,
getParentRoute: () => parentRoute,
beforeLoad,
}),
createRoute({
path: `${from}/$`,
getParentRoute: () => parentRoute,
beforeLoad,
}),
]
}
/** Creates redirect routes */
function createWildcardRedirectRoutes(
from: string,
to: string,
parentRoute = rootRoute,
) {
let fromRegexPattern = `/(?:${from})(/|$)`
if (to[0] === '/') {
// Support absolute `to` paths via leading slash
fromRegexPattern = '^.+' + fromRegexPattern
to = to.substr(1)
}
if (to.length) {
to = '/' + to
}
const fromRegex = new RegExp(fromRegexPattern)
const beforeLoad = ({ location }) => {
const newTo = location.pathname.replace(fromRegex, `${to}$1`)
throw redirect({ replace: true, to: newTo })
}

// Optional params matching isn't supported, so we need to create a route for the direct
// match, as well as one with a route splat ($)
return [
createRoute({
path: `${from}/`,
getParentRoute: () => parentRoute,
beforeLoad,
}),
createRoute({
path: `${from}/$`,
getParentRoute: () => parentRoute,
beforeLoad,
}),
]
}
eastern-cyan
eastern-cyan16mo ago
When it comes to file-based routing, its mostly an all-in sort of approach. Since, the route-tree, is build off root route, it isn't really meant to be manipulated after the fact. Honestly, for the improved DX file-based routing gives, I'd just create individual files with the redirects explicitly stated.
src/routes
create.station.index.tsx
create.station.$.tsx
src/routes
create.station.index.tsx
create.station.$.tsx
And just chuck it in a beforeLoad, or abstract these redirects away. Like:
// redirects-list.tsx
const redirects = {
"create/station": () => {
throw redirect({ to: '/somewhere' })
}
} as const

// create.station.index.tsx and create.station.$.tsx
export const Route = createFileRoute('/create/station/')({
beforeLoad: redirects['create/station']
})
// redirects-list.tsx
const redirects = {
"create/station": () => {
throw redirect({ to: '/somewhere' })
}
} as const

// create.station.index.tsx and create.station.$.tsx
export const Route = createFileRoute('/create/station/')({
beforeLoad: redirects['create/station']
})
stormy-gold
stormy-goldOP16mo ago
Thanks so much for your reply! We have at least 20-30 redirects so it will get pretty messy/verbose in the routes folder.
eastern-cyan
eastern-cyan16mo ago
If they don't get touched very often, you could chuck em all into a route-group and forget about it.
src/routes
(redirects-do-not-touch)/
create.station.index.tsx
create.station.$.tsx
src/routes
(redirects-do-not-touch)/
create.station.index.tsx
create.station.$.tsx
https://tanstack.com/router/latest/docs/framework/react/guide/file-based-routing#file-naming-conventions But yea, file-based is a bit more all in.
File-Based Routes | TanStack Router React Docs
Most of the TanStack Router documentation is written for file-based routing. This guide is mostly intended to help you understand in more detail how to configure file-based routing and the technical details behind how it works. Prerequisites
stormy-gold
stormy-goldOP16mo ago
One other small benefit of patching the routeTree as in my example is that these redirects don’t show up in the types as they’re added afterwards. So there’s no type suggestions for the many deprecated routes My workaround works, but it does feel a little dirty and that it may stop working if the behavior of “addChildren” (which maybe should be called “setChildren”) changes @Tanner Linsley any plans to allow for more native support of combining file routes and code route definitions? Being able to insert more terse code-generates routes for some usecases and relying on file-routes for the majority is really powerful
harsh-harlequin
harsh-harlequin16mo ago
what is "route based matching"?
stormy-gold
stormy-goldOP16mo ago
Thanks for asking, I see my phrasing was confusing. Updated my message, hope it makes sense now
harsh-harlequin
harsh-harlequin16mo ago
how would you envision this could look like in an example app?

Did you find this page helpful?