Skip to content

Start: the Vite plugin and tsr generate write different routeTree.gen.ts (Start footer added, then stripped) #8437

Open

Description

@TheBinaryGuy

Which project does this relate to?

Start

Describe the bug

The TanStack Start Vite plugin and the Router CLI write different routeTree.gen.ts files for the same project, and the scaffold produced by tanstack create ships both.

The Start plugin (@tanstack/start-plugin-core, start-router-plugin/route-tree-footer.ts) appends a footer to the generated route tree:

import type { getRouter } from './router.tsx'
import type { createStart } from '@tanstack/react-start'
declare module '@tanstack/react-start' {
  interface Register {
    ssr: true
    router: Awaited<ReturnType<typeof getRouter>>
  }
}

tsr generate (@tanstack/router-cli) does not know about Start and writes the file without it. So every vite build or vite dev adds the nine lines and every tsr generate removes them. In a project that commits routeTree.gen.ts, the file flips on every run of whichever tool ran last.

The scaffold's router.tsx still carries declare module '@tanstack/react-router' { interface Register { router } }, which masks the problem for the router registration. The ssr: true registration has no such fallback: after tsr generate, RegisteredSsr is false, so the serialisable-loader-result check (ValidateSerializableLifecycleResult) silently degrades to any until the next Vite run puts the footer back.

Complete minimal reproducer

https://github.com/TheBinaryGuy/tanstack-start-tsr-footer-repro (the unmodified tanstack create output plus a repro script; bun install && bun run repro shows the diff after each step). Open in StackBlitz: https://stackblitz.com/github/TheBinaryGuy/tanstack-start-tsr-footer-repro

Steps to Reproduce the Bug

npx @tanstack/cli@latest create start-ref --framework React --package-manager bun --non-interactive --no-examples --no-toolchain --deployment nitro
cd start-ref
git add -A && git commit -qm scaffold
bun run build                 # Start plugin: routeTree.gen.ts gains the 9-line footer
git diff --stat src/routeTree.gen.ts
bun run generate-routes       # the scaffold's own `tsr generate` script: footer removed again
git diff --stat src/routeTree.gen.ts
bun run build                 # footer added again
git diff --stat src/routeTree.gen.ts

Observed with @tanstack/react-start 1.168.54, @tanstack/start-plugin-core 1.171.44, @tanstack/router-cli 1.167.36 (and the same with 1.168.49 / 1.171.39 / 1.167.33).

Expected behavior

Both generators produce the same file for a Start project. Either tsr generate reads the Start footer (for example by resolving it the same way the plugin does when @tanstack/react-start is present), or the scaffold does not ship a generate-routes script that undoes what its own build writes, and the Router CLI docs say it is not for Start projects.

Platform

  • OS: macOS 27.0 (arm64)
  • Bun 1.4.0, Node 24
  • Vite 7

Additional context

The Router CLI installation docs already say to use the CLI "only if you are not using a supported bundler", which suggests the fix may be on the scaffold and docs side rather than teaching the CLI about Start. A related report about the Start plugin overriding router generator configuration is #7768.

Activity

  1. schiller-manuel commented on Sep 15, 2026

    @schiller-manuel
    Collaborator

    yes, tsr CLI is not supported for start at the moment

  2. p7gg commented on Oct 18, 2026

    @p7gg

    For anyone who needs the two in sync today, mirroring the plugin's footer into tsr.config.json works — tsr generate then writes the same file as vite dev / vite build:

    {
      "target": "react",
      "routeTreeFileFooter": [
        "import type { getRouter } from './router.tsx'\nimport type { createStart } from '@tanstack/react-start'\ndeclare module '@tanstack/react-start' {\n  interface Register {\n    ssr: true\n    router: Awaited<ReturnType<typeof getRouter>>\n  }\n}"
      ]
    }

    Notes:

    • The Start plugin replaces the generator's footer with its own (routeTreeFileFooter: getRouteTreeFileFooter in start-router-plugin), so there's no duplication in Vite mode — both tools end up writing identical output.
    • The static entry is only equivalent while the app has no src/start.ts: with one, the plugin footer also imports startInstance and adds a config line that the static entry would need to mirror.
    • Verified with @tanstack/router-cli 1.167.45, @tanstack/start-plugin-core 1.171.54, @tanstack/react-start 1.169.3, Vite 8, and Nitro 3 (nitro/vite plugin).
  3. EduKegler commented on Oct 30, 2026

    @EduKegler

    Besides the silent any described above, the missing footer can fail tsc. If src/start.ts has request middleware that adds context, the footer's config line is what types getGlobalStartContext():

    // src/start.ts
    const nonce = createMiddleware().server(({ next }) =>
      next({ context: { nonce: 'abc' } }),
    )
    
    export const startInstance = createStart(() => ({
      requestMiddleware: [nonce],
    }))
    
    // src/nonce.ts
    export const nonce = () => getGlobalStartContext()?.nonce

    After vite build, tsc passes. After tsr generate, it fails until the next Vite run:

    src/nonce.ts(3,53): error TS2339: Property 'nonce' does not exist on type 'never'.
    

    I opened TanStack/cli#526 so that new Start apps generate routes with vite build instead of tsr generate. The Router CLI docs could still say that the CLI is not for Start projects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions