I migrated a full-stack React application from Remix to React Router v7 framework mode over a weekend. The change touched 40 files and removed 591 net lines. Imports and configuration were straightforward; TypeScript took most of the real work.
This part covers why I switched and what tripped me up. Part 2 covers the step-by-step process.
Why React Router v7?
React Router v7 is Remix v3 renamed. Ryan Florence and Michael Jackson merged the projects because “Remix v2 had become such a thin wrapper around React Router that an artificial separation developed between the two projects.”
The payoff is immediate. Instead of juggling @remix-run/node, @remix-run/react, @remix-run/serve, and others, everything consolidates into react-router. My dependencies dropped from 16 to 3. The react-router typegen command eliminates manual type annotations in loader functions. The Vite-based build system is cleaner than Remix’s custom setup.
TypeScript Was the Hard Part
The API stayed compatible. TypeScript did not.
The git log tells the story — multiple commits dedicated to resolving type issues:
- LoaderFunctionArgs and useLoaderData typing: Every route needed manual type updates
- Route module type safety: New type patterns required learning React Router v7’s approach
- Interface mismatches: Database query results needed type alignment with component expectations
- Case-sensitivity bugs: Database queries failed due to case-sensitive matching that worked in Remix
The git history ended up with multiple commits devoted only to type errors. Stricter route types were useful once they worked, but they were the migration cost I had underestimated.
What Actually Changed
The migration touched 40 files with 327 insertions and 918 deletions — a net reduction of 591 lines. The main changes: package dependencies (replacing all @remix-run/* packages), build scripts (switched to react-router CLI), and Vite configuration.
The Vite config transformation was dramatic:
- import { vitePlugin as remix } from '@remix-run/dev';
- // Complex remix configuration with future flags
- remix({
- future: {
- v3_fetcherPersist: true,
- v3_relativeSplatPath: true,
- v3_throwAbortReason: true,
- v3_singleFetch: true,
- v3_lazyRouteDiscovery: true,
- }
- })
+ import { reactRouter } from '@react-router/dev/vite';
+ // Simple, clean configuration
+ plugins: [reactRouter(), tsconfigPaths(), tailwindcss()]
Every route and component needed import updates:
- import type { LoaderFunctionArgs } from '@remix-run/node';
- import { useLoaderData } from '@remix-run/react';
+ import type { LoaderFunctionArgs } from 'react-router';
+ import { useLoaderData } from 'react-router';
Loaders, actions, and components work identically. The migration was mostly changing imports and build configuration.
Field tip Route files with
.tsextensions need to become.tsxfor React Router v7 to recognize them.
The entry.server.tsx got simpler — React Router v7 removed the bot/browser request splitting logic. Bundle size dropped ~30% and build times improved with the unified package structure.
Continue to Part 2 for the detailed step-by-step migration process and React Router’s typegen feature.
One quick signal
Did this earn your time?
Thanks. That gives me something concrete to check.



