A step-by-step walkthrough for setting up TanStack Router's file-based routing in a new Vite + React + TypeScript project. By the end, adding a file to src/routes will add a page to your app.

Tech stack: Vite, React, TypeScript, TanStack Router, VS Code

You'll need: Node.js and pnpm installed. The commands use pnpm, but npm or yarn work the same way.

1. Create a Vite + React + TypeScript project

pnpm create vite@latest project-folder-name --template react-ts 

The --template react-ts flag selects React and TypeScript for you, so Vite skips those questions. If it asks anything else (for example, whether to install dependencies and start the dev server), choose what you prefer.

If dependencies weren't installed, run:

cd project-folder-name pnpm install 

2. Install the dependencies

Production dependency:

pnpm add @tanstack/react-router 

Dev dependencies:

pnpm add -D @tanstack/router-plugin @tanstack/router-cli @tanstack/react-router-devtools @types/node 

What each one does:

  • @tanstack/router-plugin: the Vite plugin that watches src/routes and generates your route tree
  • @tanstack/router-cli: provides the tsr command used in step 12
  • @tanstack/react-router-devtools: the devtools panel used in step 13
  • @types/node: Node.js type definitions

3. Configure the TanStack Router plugin

Open vite.config.ts and add the TanStack Router plugin.

Important: the TanStack Router plugin must be declared in plugins before react().

Setting autoCodeSplitting: true tells TanStack Router to automatically lazy-load your route components, so you don't have to split the code by hand.

import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' import { tanstackRouter } from '@tanstack/router-plugin/vite' export default defineConfig({ plugins: [ tanstackRouter({ target: 'react', autoCodeSplitting: true, // automatic lazy loading of route components }), react(), ], }) 

4. Make sure TypeScript includes the generated route tree

The generated routeTree.gen.ts file describes your whole routing hierarchy. Once TypeScript can see it, your route paths become type safe, which you'll try out in step 11.

Open tsconfig.app.json and check that the generated route tree is covered by include:

{ // ... "include": [ "src", "./src/routeTree.gen.ts" // Ensures TypeScript picks up the autogenerated tree instantly ] } 

The Vite template's "src" entry already covers src/routeTree.gen.ts, so the second line is a safeguard rather than a requirement. It's useful if you've changed include in your own project.

5. Create the routes folder

Inside src/, create the following folder and files:

src/routes src/routes/__root.tsx src/routes/index.tsx 

The TanStack Router plugin watches this folder. Every file you add here becomes a route.

6. Start the dev server

pnpm dev 

When the dev server starts, the TanStack Router plugin does two things automatically:

  • Populates __root.tsx and index.tsx with starter code
  • Generates a new file, src/routeTree.gen.ts

You won't see any change in the browser yet. That comes after step 8.

7. Protect the generated route tree file

src/routeTree.gen.ts is generated for you and should never be edited by hand. To protect it, add the following to your VS Code .vscode/settings.json (create the file if it doesn't exist):

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

Click the file in the VS Code file explorer and you should see a lock icon to the right of its name.

8. Wire up the router in App.tsx

Replace the contents of src/App.tsx with:

import { RouterProvider, createRouter } from "@tanstack/react-router"; import { routeTree } from "./routeTree.gen"; const router = createRouter({ routeTree }); declare module "@tanstack/react-router" { interface Register { router: typeof router; } } function App() { return <RouterProvider router={router} />; } export default App; 

The declare module block registers your router with TypeScript, so links and route paths are type-checked across the whole app.

9. See it in the browser

Open http://localhost:5173 in your browser. You should now see:

Hello "__root"! Hello "/"! 

What's happening:

  • __root.tsx renders the first line. The root route always appears, no matter which page you're on, which makes it the place for things like your navigation menu and website logo. Open the file and you'll find an <Outlet /> component. This is where the currently matched route is rendered.
  • index.tsx renders the second line, through the root route's <Outlet />. It's the default route, shown at the root path, localhost:5173/.

10. Test the setup

Create about.tsx and info.tsx in the src/routes folder.

The TanStack Router plugin configured in vite.config.ts automatically populates these files with starter code and registers them in routeTree.gen.ts. That in turn updates the router used by <RouterProvider> in App.tsx, so there's nothing else to wire up.

Go to your browser and navigate to localhost:5173/about and localhost:5173/info. (If Vite chose a different port, use the one shown in your terminal.)

11. Try type-safe navigation

One of the best things about TanStack Router is that your route paths are type checked. Let's see it in action.

Create test-routes.tsx in the src/routes folder. The plugin fills it with starter code, so replace everything in it with:

import { createFileRoute, Link, useNavigate } from '@tanstack/react-router' export const Route = createFileRoute('/test-routes')({ component: RouteComponent, }) function RouteComponent() { const navigate = useNavigate() return ( <> <div>Hello "/test-routes"!</div> {/* Declarative navigation with <Link> */} <Link to="/info">Go to Info</Link> {/* Programmatic navigation with useNavigate */} <button type="button" onClick={() => navigate({ to: '/some-file' })}> Go to Some File </button> </> ) } 

Notice that to: '/some-file' is flagged as an error in your editor. There is no some-file.tsx in src/routes, so the path doesn't exist, and TypeScript catches the mistake before you run the app. (The error shows up in your editor and in tsc, not in the browser.)

Now change '/some-file' to '/info', or delete the text between the quotes and trigger autocomplete. Your editor will list every route in your app, and the error disappears. The to prop on <Link> is type safe in the same way.

Visit localhost:5173/test-routes to try both.

12. Generate routes without the dev server

You can also generate the route tree manually when the dev server isn't running. Add these to the scripts section of your package.json:

{ "scripts": { "generate-routes": "tsr generate", "watch-routes": "tsr watch" } } 
  • pnpm generate-routes generates routeTree.gen.ts once.
  • pnpm watch-routes regenerates it whenever your route files change.

You won't need these day to day, because pnpm dev already keeps the route tree up to date. They're handy for troubleshooting, for example if the generated file gets out of sync.

13. Add the TanStack Router devtools

The devtools are a great way to see how your routes fit together. Modify src/routes/__root.tsx to look like this:

import * as React from 'react' import { Outlet, createRootRoute } from '@tanstack/react-router' import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' export const Route = createRootRoute({ component: RootComponent, }) function RootComponent() { return ( <React.Fragment> <div>Hello "__root"!</div> <Outlet /> <TanStackRouterDevtools position="bottom-right" /> </React.Fragment> ) } 

A devtools button will appear at the bottom right of your browser. Click it to see the hierarchy of the routes (pages) in your website and how they connect. It's an excellent aid for learning how TanStack Router works.

Wrapping up

That's it. You now have file-based routing with TanStack Router running on Vite, React, and TypeScript, where adding a file to src/routes adds a page to your app.

If you have any suggestions, leave a comment and I'll do my best to respond. Thanks for reading!