If you’ve been building with TanStack Start recently, you’ve probably run into strange and confusing errors like:
app.config.timestamp_175040…
duplicating in your project folder every time the dev server starts@tanstack/react-start/config
not exporting defineConfig
These errors can seem vague and directionless, and given how relatively new TanStack Start is, there’s very limited information available about them online. Even good ol’ Stack Overflow had unanswered questions about the same issues. The only useful lead I found was a GitHub issue on the official Start repository, which I just happened to stumble upon.
Many of the bugs and errors mentioned above came up in that discussion, and according to a contributor who responded with helpful insight, these issues were caused by breaking changes introduced in Start’s recent migration from Vinxi to Vite, specifically in the v1.121.0 release.
The good news?
You can avoid many of these problems by pinning your package versions. Locking in specific versions of the core Start libraries and dependencies prevents your project from unexpectedly pulling in breaking updates, especially when major internal changes like these happen:
However, version pinning is often an afterthought for many developers, and this is the kind of trouble we run into as a result of that oversight. If you’ve already taken steps to lock your dependencies, you likely have nothing to worry about. That is, unless you’re intentionally trying to update, in which case, this article will be especially helpful.
In this guide, we’ll explore the probable reasoning behind the migration to Vite and walk you through the process of migrating your TanStack Start project to be fully compatible with the new Vite-based setup.
The exact reason for the migration hasn’t been formally stated, at least at the time of writing. But it’s fair to assume the shift was motivated by the many advantages Vite offers over Vinxi.
While Vinxi isn’t a direct competitor to Vite, since it’s built on top of Vite, it provides a more opinionated and integrated full-stack development experience, abstracting away much of the complexity of bridging client and server logic.
That said, it seems the TanStack team found that a direct integration with Vite offered greater flexibility and low-level control, particularly over the client-side build process and development server behavior. This likely allowed them to create a simpler, more streamlined architecture for Start, with fewer dependencies and without the extra layer of abstraction that Vinxi introduces.
Another, and possibly the most significant factor, could be Vite’s massive and rapidly growing community. By aligning more closely with Vite, TanStack Start can benefit from the rich plugin ecosystem, ongoing improvements, and broader community support that Vite enjoys.
It’s important to note that this move doesn’t diminish Vinxi’s value. It remains a powerful tool for building full-stack frameworks. Rather, this shift highlights an important fact: different projects have different needs, and sometimes the right decision is about choosing the tool that best fits your project’s goals and architecture.
The v1.121.0 update merged in an alpha build known as “devinxi,” which had been in development for a while. So, if you were already building on that alpha version, you’re likely in the clear. This migration guide is intended for those of us who were still using the stable Vinxi-based setup before this release.
N.B., before making any changes, be sure to back up your project. If your app is in a working state, commit all changes to version control (e.g., Git). Even if it’s not fully functional, it’s still worth saving your current state; migrations like this can get messy, and having a fallback can save you hours of debugging.
As the name of the update implies, the first step is to remove all Vinxi-related packages and scripts and replace them with Vite.
To do that, run the following commands:
npm uninstall vinxi npm install vite
Next, update the scripts in your package.json
to use Vite’s standard dev
and build
commands:
{ "scripts": { "dev": "vite dev --port 3000", "build": "vite build", "start": "node .output/server/index.mjs" } }
Another important step is to remove the old @tanstack/start
package and replace it with the new @tanstack/react-start
package. You can do this by running the following commands:
npm uninstall @tanstack/start npm install @tanstack/react-start
This is not a Vinxi-related change. The framework has moved to framework-specific adapters, which, if my guess is correct, means that @tanstack/react-start
is specific to React, unlike the former @tanstack/start
, which may have implied the package is framework-agnostic.
Ultimately, @tanstack/start
is deprecated and will no longer receive updates, so it is advisable to uninstall it and install the latter.
While you may not have had any reason to use the app.config.ts
configuration file, it is present in your project, specifically in the root directory. This file defines the application’s build and server-side behavior and is where custom changes can be made to those configurations.
However, because Start is now a Vite plugin, the app.config.ts
file is redundant and should be replaced with a vite.config.ts
file, where Start can be registered as a Vite plugin.
To do this, delete the app.config.ts
file in your project directory and add a vite.config.ts
file with the following code:
// vite.config.ts import { defineConfig } from 'vite' import tsconfigPaths from 'vite-tsconfig-paths' import { tanstackStart } from '@tanstack/react-start/plugin/vite' export default defineConfig({ plugins: [ tsconfigPaths(), // you can register other plugins e.g tailwindcss() tanstackStart() ] })
Now, to make custom configurations to your TanStack Start project, you can pass an optional configuration object as an argument when invoking the TanStack Start plugin and include your custom options there:
export default defineConfig({ plugins: [ tanstackStart({ /** Add your options here */ }) ] })
In line with Vite’s convention, the default base application directory has been changed from app
to src
. Therefore, ensure that all your source code is located within or moved to the src
folder. Additionally, if you use a path alias in your tsconfig.json
, make sure to update it to point to src
:
"paths": { "@/*": [ "./src/*" ] },
There have been significant changes to the router API. Notably, the ssr.tsx
and client.tsx
files are no longer required and should be deleted from the src
folder.
However, if your project depends on the ssr.tsx
file, for example, for configuring authentication, and you’ve made custom modifications to it, you don’t need to delete it. Instead, rename it to server.tsx
.
Just make sure the overall structure is different from this:
// src/server.tsx import { createStartHandler, defaultStreamHandler } from '@tanstack/react-start/server' import { createRouter } from './router' export default createStartHandler({ createRouter, })(defaultStreamHandler)
There are no required changes to the router.tsx
file; you only need to make sure it exists in your project and that the createRouter
function is properly exported:
// src/router.tsx import { createRouter as createTanStackRouter } from '@tanstack/react-router' import { routeTree } from './routeTree.gen' export function createRouter() { const router = createTanStackRouter({ routeTree, scrollRestoration: true, }) return router } declare module '@tanstack/react-router' { interface Register { router: ReturnType<typeof createRouter> } }
Perhaps the most significant breaking change in this update is the transition from API route handlers to what are now called Server Routes. The createAPIFileRoute()
function has been deprecated and replaced by the new createServerFileRoute()
function.
To apply these changes, begin by deleting the api.ts
entry handlers located in the src
folder. Then, update your API routes by replacing all instances of createAPIFileRoute()
with createServerFileRoute()
.
For example, if you previously had an API route defined as follows:
// src/routes/api/users.tsx import { json } from '@tanstack/react-start' import { createAPIFileRoute } from '@tanstack/react-start/api' import { fetchUsers } from '../../utils/users' export const APIRoute = createAPIFileRoute('/api/users')({ GET: async ({ request, params }) => { console.info('Fetching users... @', request.url) const users = await fetchUsers() return json(users) }, })
… you must convert it to a server route like this:
// src/routes/api/users.tsx import { json } from '@tanstack/react-start' import { createServerFileRoute } from '@tanstack/react-start/server' import { fetchUsers } from '../../utils/users' export const ServerRoute = createServerFileRoute(“/api/users”).methods({ GET: async ({ request, params }) => { console.info('Fetching users... @', request.url) const users = await fetchUsers() return json(users) }, })
And that’s it!
Once you’ve made all the necessary updates, the final step is to delete the node_modules
folder and the package-lock.json
(or pnpm-lock.yaml
, etc.) file from your project directory. Then reinstall your dependencies, and everything should work as expected.
Migrations like this can often get messy, especially when they involve core aspects of the framework.
Because you’re dealing with foundational changes, the errors thrown during migration are often vague or misleading. For example, while migrating from API Routes to the new Server Routes, I initially assumed there was no need to pass a path argument to the createServerFileRoute()
function, based on how it was shown in the official migration guide:
However, this particular mistake didn’t even throw an error until the server route was invoked at runtime. These kinds of subtle issues are easy to overlook during a migration like this.
Fortunately, TypeScript does a great job of highlighting files and modules with potential issues. You can leverage TypeScript’s static analysis to catch many of these mistakes early, helping to make your transition smoother and with fewer runtime errors.
As I mentioned earlier, pinning the versions of core packages in your project would have made the transition smoother and less rushed. The same advice applies to those who are not ready to migrate yet; it’s crucial to prevent these packages from updating automatically and potentially breaking your application.
To simplify this process, the TanStack team has built a simple helper function into create-start-app
that automatically pins the correct versions for you. To use it, simply run the following command in your terminal:
npx create-start-app@latest pin-versions
This will update your package.json
file with the appropriate pinned versions, helping you maintain a stable development environment until you’re ready to migrate:
The best way to avoid disruptive updates like this in the future is to stay on top of new releases. Given the infancy of the TanStack Start ecosystem, the most reliable way to stay informed is by joining the official TanStack Start Discord server.
There, you’ll get information straight from the source and benefit from real-time support from the core team and other community members.
This guide walked you through the essential steps needed to update your TanStack Start project to be fully compatible with the new Vite-based setup. We covered everything from dependency adjustments and configuration file updates to router and server route changes.
If you’re just getting started or want to see the new setup in action, the official starter templates are the easiest way to dive in. The TanStack team has updated these templates to reflect the latest changes and provide the best possible experience for new users.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowThis article covers how frontend chaos engineering helps catch UI and UX issues before users experience them using tools like gremlins.js.
Deno 2.4 isn’t just a maintenance update; it’s a statement. Learn about the most impactful changes in Deno 2.4, including the return of a first-party bundler and new spec-aligned ways to handle assets.
The AI freight train shows no signs of slowing down. Seven senior developers discuss how frontend devs can make themselves indispensable in the age of AI.
It’s never been a better time to be an Angular developer. Reflect on the highlights of Angular’s evolution from its early days to the recent v20 release.