Which project does this relate to?
Start
Describe the bug
Generator plugins passed as tanstackStart({ router: { plugins: [...] } }) never reach the route generator — no hook (init, onRouteTreeChanged, afterTransform) is ever invoked, in dev or build, with no error or warning.
Cause: tanStackStartRouter builds its internal generator-plugin list and places it after ...routerConfig in the config it hands to tanstackRouterGenerator, so the user's plugins key is overwritten:
https://github.com/TanStack/router/blob/4eed408f12e0dee55708b1e2b53fefb3441c414d/packages/start-plugin-core/src/vite/start-router-plugin/plugin.ts#L147-L159
tanstackRouterGenerator(() => {
const routerConfig = getConfig().startConfig.router
const plugins = [clientTreeGeneratorPlugin, routesManifestPlugin()]
if (startPluginOpts.prerender?.enabled === true) {
plugins.push(prerenderRoutesPlugin())
}
return {
...routerConfig, // includes the user's routerConfig.plugins…
target: corePluginOpts.framework,
routeTreeFileFooter: getRouteTreeFileFooter,
plugins, // …which is clobbered here
}
}, routerPluginContext)
The same options work correctly with the plain tanstackRouter Vite plugin (no Start), where user config flows through getConfig untouched — so this is Start-specific.
Suggested fix (verified locally via a patched package):
if (routerConfig.plugins) plugins.push(...routerConfig.plugins)
Your Example Website or App
// vite.config.ts — minimal repro
import { defineConfig } from 'vite'
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
export default defineConfig({
plugins: [
tanstackStart({
router: {
plugins: [
{
name: 'probe',
init: () => console.error('[probe] init'),
onRouteTreeChanged: () => console.error('[probe] onRouteTreeChanged'),
},
],
},
}),
],
})
Steps to Reproduce the Bug or Issue
- Add the probe generator plugin above to any TanStack Start app's
vite.config.ts
- Run
vite dev or vite build
- Neither
[probe] line is printed; with the plain tanstackRouter() plugin (non-Start), both print
Expected behavior
User-supplied router.plugins are merged with Start's internal generator plugins and their hooks fire on generator runs — or, if generator plugins are intentionally unsupported under Start, a loud error instead of silent acceptance.
Platform
- @tanstack/react-start: 1.168.27 (start-plugin-core 1.171.19; bug still present on
main)
- OS: macOS
Additional context
Footnote once plugins do run: createRouterGeneratorPlugin wraps generator.run() in catch (e) { console.error(e) }, so a generator plugin that throws (e.g. to enforce a project invariant) can log but never fail the build. May be intentional, but it compounds the silence here.
Which project does this relate to?
Start
Describe the bug
Generator plugins passed as
tanstackStart({ router: { plugins: [...] } })never reach the route generator — no hook (init,onRouteTreeChanged,afterTransform) is ever invoked, in dev or build, with no error or warning.Cause:
tanStackStartRouterbuilds its internal generator-plugin list and places it after...routerConfigin the config it hands totanstackRouterGenerator, so the user'spluginskey is overwritten:https://github.com/TanStack/router/blob/4eed408f12e0dee55708b1e2b53fefb3441c414d/packages/start-plugin-core/src/vite/start-router-plugin/plugin.ts#L147-L159
The same options work correctly with the plain
tanstackRouterVite plugin (no Start), where user config flows throughgetConfiguntouched — so this is Start-specific.Suggested fix (verified locally via a patched package):
Your Example Website or App
Steps to Reproduce the Bug or Issue
vite.config.tsvite devorvite build[probe]line is printed; with the plaintanstackRouter()plugin (non-Start), both printExpected behavior
User-supplied
router.pluginsare merged with Start's internal generator plugins and their hooks fire on generator runs — or, if generator plugins are intentionally unsupported under Start, a loud error instead of silent acceptance.Platform
main)Additional context
Footnote once plugins do run:
createRouterGeneratorPluginwrapsgenerator.run()incatch (e) { console.error(e) }, so a generator plugin that throws (e.g. to enforce a project invariant) can log but never fail the build. May be intentional, but it compounds the silence here.