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 watchessrc/routesand generates your route tree -
@tanstack/router-cli: provides thetsrcommand 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
pluginsbeforereact().
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.tsxandindex.tsxwith 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.tsxrenders 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.tsxrenders 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-routesgeneratesrouteTree.gen.tsonce. -
pnpm watch-routesregenerates 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!
