Router CLI

Purpose and Scope

The Router CLI is the file-based routing command-line path for TanStack Router projects that cannot use a supported bundler integration. Its job is deliberately narrow: it generates the route tree file from route files and can watch those files during development. That generated route tree is then imported by the framework router package, passed into createRouter, and registered for type safety in the application entry point. The CLI is therefore not a replacement for the Router runtime, a dev server, or the full TanStack CLI application generator; it is the standalone route-tree generation bridge for file-based routing.

Sources: docs/router/installation/with-router-cli.md, e2e/react-router/generator-cli-only/src/main.tsx, e2e/solid-router/generator-cli-only/src/main.tsx, e2e/vue-router/generator-cli-only/src/main.tsx

This distinction matters because TanStack has more than one command-line surface. The broader TanStack CLI can scaffold applications, list add-ons, search docs, and create Router-only projects, while the Router CLI package documented here is installed as @tanstack/router-cli and exposes the tsr command for route generation. In day-to-day Router work, choose the Router CLI only when your build setup does not already provide route generation through a supported bundler plugin. If you are using the plugin path, the plugin usually owns generation and additional build-time features.

Relevant Source Files

  • docs/router/installation/with-router-cli.md — documents when to use @tanstack/router-cli, how to wire tsr generate and tsr watch, framework-specific notes, generated-file ignore guidance, and visible default configuration fields.
  • e2e/react-router/generator-cli-only/src/main.tsx — shows a React app consuming ./routeTree.gen, creating a router with runtime options, registering the router type, and rendering RouterProvider.
  • e2e/react-router/generator-cli-only/src/routes/index.tsx — shows the minimal React file route contract using createFileRoute('/') and an exported Route.
  • e2e/solid-router/generator-cli-only/src/main.tsx — shows the same generated-route-tree integration pattern for @tanstack/solid-router with Solid rendering.
  • e2e/solid-router/generator-cli-only/src/routes/index.tsx — shows the minimal Solid file route contract using createFileRoute('/') and Solid JSX conventions.
  • e2e/vue-router/generator-cli-only/src/main.tsx — shows the same generated-route-tree integration pattern for @tanstack/vue-router with Vue createApp and RouterProvider.

When to Use the Router CLI

Use @tanstack/router-cli when you want file-based routing but are not using a supported bundler integration. The installation documentation states this as a warning, not a preference: the CLI only supports generation of the route tree file and does not provide other features. That means it is best understood as the minimum viable integration between the filesystem route convention and the typed Router runtime. It scans the configured routes directory, writes a generated route tree module, and lets the framework package consume that module like any other source file.

Sources: docs/router/installation/with-router-cli.md

A typical Router application depends on the generated route tree as an application contract. The route files define paths, components, loaders, and options; the generated routeTree.gen.ts file connects those route definitions into the tree that createRouter needs. The e2e fixtures show this contract directly: the app imports routeTree from ./routeTree.gen, passes it to createRouter, and then renders a RouterProvider with that router. The CLI is not involved at runtime after generation, but the runtime depends on the generated module being current.

Sources: e2e/react-router/generator-cli-only/src/main.tsx, e2e/solid-router/generator-cli-only/src/main.tsx, e2e/vue-router/generator-cli-only/src/main.tsx

Installation and Script Wiring

Install the package as a development dependency for the framework you are using. The Router installation page lists @tanstack/router-cli for React and Solid in the package-manager install tabs, and the same generated-route-tree pattern is exercised in the Vue e2e fixture. After installation, wire the CLI into package.json scripts so route generation happens before builds and route watching runs during development. The documentation recommends script names such as generate-routes for tsr generate and watch-routes for tsr watch, then composing those scripts into build and dev.

Sources: docs/router/installation/with-router-cli.md

{
  "scripts": {
    "generate-routes": "tsr generate",
    "watch-routes": "tsr watch",
    "build": "npm run generate-routes && ...",
    "dev": "npm run watch-routes && ..."
  }
}

The composition is important because file-based routing introduces a generated source artifact. In production builds, run tsr generate first so the compiler, bundler, and type checker see a route tree that matches the current route files. In development, run tsr watch alongside the application server so adding, removing, renaming, or editing route files regenerates the route tree as the application changes. The docs describe watch as continuously watching configured directories and regenerating routes as needed, which aligns with the route tree being an implementation artifact rather than a hand-authored module.

Command Reference

CommandPurposeTypical lifecycle
tsr generateGenerates routes for a project based on the provided configuration.Run before build, CI type checks, or any task that needs a current generated route tree.
tsr watchWatches configured route directories and regenerates routes when files are added, removed, or changed.Run during local development with the app dev server.

The public command entrypoint exposed to projects is tsr. The two documented subcommands are intentionally simple. generate is the deterministic one-shot command for producing the generated route tree, while watch is the long-running development command. Both operate on the same file-based routing configuration. The generated output is then imported from application code as a normal module; the React, Solid, and Vue fixtures all import routeTree from ./routeTree.gen, which matches the documented default output path of ./src/routeTree.gen.ts.

Sources: docs/router/installation/with-router-cli.md, e2e/react-router/generator-cli-only/src/main.tsx, e2e/solid-router/generator-cli-only/src/main.tsx, e2e/vue-router/generator-cli-only/src/main.tsx

Configuration and Generated File Hygiene

The CLI has defaults intended to work for common projects. The visible React configuration defaults use routesDirectory set to ./src/routes, generatedRouteTree set to ./src/routeTree.gen.ts, routeFileIgnorePrefix set to -, quoteStyle set to single, and target set to react. The Solid section also calls out TypeScript settings for JSX preservation and jsxImportSource set to solid-js. These defaults reinforce the intended source layout: human-authored route files live under src/routes, while the generated tree sits next to the app source and should be treated as generated code.

Sources: docs/router/installation/with-router-cli.md

{
  "routesDirectory": "./src/routes",
  "generatedRouteTree": "./src/routeTree.gen.ts",
  "routeFileIgnorePrefix": "-",
  "quoteStyle": "single",
  "target": "react"
}

Because routeTree.gen.ts is managed by TanStack Router, do not hand-edit it and do not let formatting or linting workflows treat it like ordinary source. The installation page recommends ignoring the generated route tree in tools such as Prettier, ESLint, or Biome. It also gives VSCode settings that mark the file read-only and exclude it from search and file watching. That advice prevents a common failure mode: an editor or formatter opens or mutates a generated file after a route rename, leaving developers debugging generated output instead of fixing the route files that produced it.

{
  "files.readonlyInclude": {
    "**/routeTree.gen.ts": true
  },
  "files.watcherExclude": {
    "**/routeTree.gen.ts": true
  },
  "search.exclude": {
    "**/routeTree.gen.ts": true
  }
}

System-to-Code Mapping

The end-to-end fixtures show the contract from route file to running app. In React, src/routes/index.tsx imports createFileRoute from @tanstack/react-router, exports Route, and binds the root path / to a Home component. The application entry point imports that generated tree, creates a router with defaultPreload: 'intent', defaultStaleTime: 5000, and scrollRestoration: true, then renders RouterProvider. The module augmentation of @tanstack/react-router registers the concrete router type, which is the step that lets route-aware APIs infer the app’s generated route structure.

Sources: e2e/react-router/generator-cli-only/src/main.tsx, e2e/react-router/generator-cli-only/src/routes/index.tsx

Solid follows the same architecture with framework-specific imports. Its route file imports createFileRoute from @tanstack/solid-router, exports Route = createFileRoute('/')({ component: Home }), and uses Solid JSX attributes in the component. Its entry point imports RouterProvider, createRouter, and routeTree, registers the router type through module augmentation of @tanstack/solid-router, and renders the provider with solid-js/web. The CLI-generated file is therefore framework-neutral in its lifecycle but framework-targeted in its generated output and imports.

Sources: e2e/solid-router/generator-cli-only/src/main.tsx, e2e/solid-router/generator-cli-only/src/routes/index.tsx

The Vue fixture confirms that the same route tree contract applies outside React and Solid. It imports RouterProvider and createRouter from @tanstack/vue-router, imports routeTree from ./routeTree.gen, creates the router, registers its type with module augmentation, and mounts a Vue app that renders the provider. The important cross-framework invariant is that the CLI produces the route tree, the framework package owns rendering and runtime navigation, and the application entry point joins them by passing routeTree into createRouter.

Sources: e2e/vue-router/generator-cli-only/src/main.tsx

Execution Flow

A CLI-only file-based Router workflow has four phases. First, author route modules under the configured routes directory, such as src/routes/index.tsx. Each route module exports a Route created by createFileRoute, which gives the generator a structured route definition to include in the tree. Second, run tsr generate or keep tsr watch running so the route modules are converted into routeTree.gen.ts. Third, import routeTree into the application entry point and create the router with any runtime options your app needs. Fourth, render the framework provider so navigation, matching, loaders, and typed APIs operate from that tree.

Sources: docs/router/installation/with-router-cli.md, e2e/react-router/generator-cli-only/src/routes/index.tsx, e2e/react-router/generator-cli-only/src/main.tsx

In the fixtures, runtime options are configured after generation, not in the CLI. defaultPreload: 'intent' configures preload behavior, defaultStaleTime: 5000 configures default cache freshness, and scrollRestoration: true enables scroll restoration. Those values demonstrate that the CLI does not replace router configuration; it only supplies the route tree input. Keep CLI configuration focused on where route files live and where generated output goes, and keep navigation, preloading, stale-time, context, and rendering concerns in the router instance and route definitions.

Testing Signals and Practical Next Steps

The generator-cli-only e2e fixtures are valuable because they prove the narrow CLI path works across framework packages. They do not rely on the bundler plugin to supply the route tree; instead, each app imports the generated routeTree.gen module directly and wires it into its framework-specific RouterProvider. This is the pattern to copy when validating a non-standard build environment: make sure route files export Route, make sure the generated file exists before the app compiles, and make sure module augmentation registers the router type for the package you are using.

Sources: e2e/react-router/generator-cli-only/src/main.tsx, e2e/solid-router/generator-cli-only/src/main.tsx, e2e/vue-router/generator-cli-only/src/main.tsx

For a new CLI-only setup, start by installing @tanstack/router-cli, adding generate-routes and watch-routes scripts, and creating a minimal src/routes/index.tsx with createFileRoute('/'). Run tsr generate, confirm src/routeTree.gen.ts exists, import it in your app entry point, and pass it to createRouter. Once the application renders, add ignore rules for the generated file and decide whether a supported bundler plugin would be a better long-term fit. For related topics, read File-Based Routing for route conventions and Router Plugin and Route Generation for the bundler-integrated path.