Writing Technical guide

Web Development · 3 min read

Migrating from Remix to React Router v7 - Part 2

The migration took two focused sessions: dependencies first, then 40 files of imports and route types. Typegen was the part worth keeping.

In this article3 sections

The migration took two focused sessions. Dependency and configuration changes were quick; updating imports and resolving route types across 40 files consumed most of the time.

Part 1 covers why I migrated and the main challenges. This part is the sequence I followed.

The Migration Sequence

Phase 1: Dependencies and Configuration (30 minutes)

Replace all @remix-run/* packages with React Router equivalents in package.json. Change build/dev commands to use the react-router CLI. Simplify Vite configuration. Add react-router.config.ts for framework-specific settings.

Phase 2: Import Refactoring (90 minutes)

This phase ate the most time. Every file importing from @remix-run/* needed updates:

  • Route files (23 files): Updated loader/action imports and useLoaderData calls
  • Component files (8 files): Changed Link and Form imports
  • Entry files (2 files): Updated server/client entry points
  • Utility files (7 files): Modified auth and theme utilities

Start with entry points, then routes, then components. Fix TypeScript errors as they appear.

Phase 3: Configuration Fine-tuning (30 minutes)

Updated TypeScript config to use React Router types. Reviewed splat routing: in the standard flat-file convention, api.auth.$.ts is a splat resource route; $rest is a named single-segment parameter, not its replacement. Added the react-router typegen command to dev scripts.

Typegen Changes Everything

Generate the route types react-router typegen automatically generates route-specific TypeScript types, eliminating manual interface definitions and preventing type drift.

This feature didn’t exist in Remix. It’s a React Router v7 innovation.

The typegen command generates a +types/<route-file>.d.ts for each route. You get automatic type inference for loader data, route-specific parameter typing, action and loader return type validation, and component prop type safety — all from your actual route implementation.

Remix already supported useLoaderData<typeof loader>. The new feature is generated route-module types, including typed component props and parameters:

import type { Route } from './+types/recipes';

export async function loader({ request }: Route.LoaderArgs) {
  return { recipes: await getRecipes(), user: await getUser(request) };
}

export default function RecipeList({ loaderData }: Route.ComponentProps) {
  const { recipes, user } = loaderData;
  return <pre>{JSON.stringify({ recipes, user }, null, 2)}</pre>;
}

The official Remix upgrade guide describes the dependency and type-generation changes. These types check the contract in TypeScript; they do not validate arbitrary runtime input.

Worth the Switch?

React Router v7 is actively developed. Remix v2 is in maintenance mode. The simplified dependency tree, better TypeScript integration, and improved build system pay off immediately.

For teams on Remix v2 with future flags enabled, the migration is straightforward. AI assistance was critical for batch import updates across 40+ files. What could have been a full day of manual work became two focused sessions.

The application now runs on React Router v7 with fewer dependencies and route types generated from the code. That was enough to justify the weekend.

Was this useful?

What was missing?

Thanks for the feedback.