Documentation Index
Fetch the complete documentation index at: https://mintlify.com/remix-run/react-router/llms.txt
Use this file to discover all available pages before exploring further.
Type Generation
React Router Framework Mode automatically generates TypeScript types from your route configuration, providing type safety for route parameters, loader data, and more.
Overview
The type generation system:
- Generates types from your
routes.ts configuration
- Creates route-specific types for params, loader data, and actions
- Updates automatically during development
- Provides IDE autocomplete for route paths and params
Setup
Type generation is enabled automatically when you use the Vite plugin:
// vite.config.ts
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [reactRouter()],
});
Generated Files
Types are generated in .react-router/types/:
.react-router/
└── types/
├── react-router-config.d.ts # Future flags config
├── +server.d.ts # Server build types
└── app/ # Route module types
├── root.d.ts
├── routes/
│ ├── _index.d.ts
│ └── posts.$id.d.ts
└── ...
Important: Do not edit these files directly. They are regenerated automatically.
Add to .gitignore
Add the generated types directory to .gitignore:
TypeScript Configuration
Update your tsconfig.json to include generated types:
{
"include": [
"**/*.ts",
"**/*.tsx",
".react-router/types/**/*"
],
"compilerOptions": {
"rootDirs": [".", "./.react-router/types"]
}
}
Route Module Types
Each route module gets generated type annotations:
// app/routes/posts.$id.tsx
import type { Route } from "./+types/posts.$id";
export async function loader({ params }: Route.LoaderArgs) {
// params.id is typed as string
const post = await getPost(params.id);
return { post };
}
export default function Post({ loaderData }: Route.ComponentProps) {
// loaderData.post is typed from loader return value
return <article>{loaderData.post.title}</article>;
}
Available Types
Each route module exports these types:
Route.LoaderArgs
Arguments passed to the loader function:
export async function loader({ params, request, context }: Route.LoaderArgs) {
// params: typed route parameters
// request: Request object
// context: server context
}
Route.ActionArgs
Arguments passed to the action function:
export async function action({ params, request, context }: Route.ActionArgs) {
const formData = await request.formData();
// ...
}
Route.ComponentProps
Props for the default route component:
export default function Component({ loaderData, actionData }: Route.ComponentProps) {
// loaderData: typed return value from loader
// actionData: typed return value from action
}
Route.ErrorBoundaryProps
Props for the ErrorBoundary component:
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
// error: unknown
return <div>Error: {String(error)}</div>;
}
Route.HydrateFallbackProps
Props for the HydrateFallback component:
export function HydrateFallback(props: Route.HydrateFallbackProps) {
return <div>Loading...</div>;
}
Route.MiddlewareArgs
Arguments for middleware functions (with future.v8_middleware flag):
export async function middleware({ params, request, context }: Route.MiddlewareArgs) {
// Middleware logic
}
Route.ClientMiddlewareArgs
Arguments for client middleware:
export async function clientMiddleware({ params, request, context }: Route.ClientMiddlewareArgs) {
// Client middleware logic
}
Route Parameters
Parameters are automatically typed based on route paths:
// routes.ts
import { route } from "@react-router/dev/routes";
export default [
route("posts/:postId/comments/:commentId", "./post-comment.tsx"),
];
// app/post-comment.tsx
import type { Route } from "./+types/post-comment";
export async function loader({ params }: Route.LoaderArgs) {
// params.postId: string
// params.commentId: string
const { postId, commentId } = params;
}
Loader Data Types
Loader return values are automatically inferred:
export async function loader() {
const posts = await db.post.findMany();
return { posts }; // Type is inferred
}
export default function Posts({ loaderData }: Route.ComponentProps) {
// loaderData.posts has the correct type
return (
<ul>
{loaderData.posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
Action Data Types
Action return values are also typed:
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData();
const post = await createPost(formData);
return { post, success: true };
}
export default function NewPost({ actionData }: Route.ComponentProps) {
// actionData?.post and actionData?.success are typed
if (actionData?.success) {
return <p>Post created: {actionData.post.title}</p>;
}
}
Manual Type Generation
Run type generation manually:
Watch Mode
Types are automatically regenerated during development when:
- Route files are added or removed
routes.ts configuration changes
react-router.config.ts changes
Future Flags Types
Future flags are typed in .react-router/types/react-router-config.d.ts:
// Generated based on your config
declare module "react-router" {
interface Future {
v8_middleware: true; // or false based on your config
v8_splitRouteModules: false;
v8_viteEnvironmentApi: false;
}
}
This enables conditional types based on enabled features.
Server Build Types
The server build exports are typed in +server.d.ts:
import type { ServerBuild } from "react-router";
// Type for your server build
export const build: ServerBuild;
Middleware Types
With middleware enabled:
export default {
future: {
v8_middleware: true,
},
} satisfies Config;
You get typed middleware functions:
import type { Route } from "./+types/route-name";
export const middleware: Route.MiddlewareFunction = async ({ params, request, context }) => {
// All arguments are typed
};
export const clientMiddleware: Route.ClientMiddlewareFunction = async ({ params, request, context }) => {
// Client middleware types
};
Type Safety Best Practices
1. Use Route Types
Always import and use the generated route types:
import type { Route } from "./+types/route-name";
export async function loader(args: Route.LoaderArgs) {
// Typed automatically
}
2. Type Return Values Explicitly
For complex return types, add explicit types:
interface LoaderData {
posts: Post[];
page: number;
totalPages: number;
}
export async function loader(): Promise<LoaderData> {
// Return type is explicit
return {
posts: await getPosts(),
page: 1,
totalPages: 10,
};
}
3. Use Type Guards
For error boundaries:
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
if (error instanceof Error) {
return <div>Error: {error.message}</div>;
}
return <div>Unknown error</div>;
}
Troubleshooting
Types Not Updating
- Check that
.react-router/types is included in tsconfig.json
- Restart your TypeScript server in VS Code (
Cmd+Shift+P > “Restart TS Server”)
- Run
npx react-router typegen manually
Build Errors
If types cause build errors:
- Delete
.react-router/types directory
- Run
npx react-router typegen
- Restart your IDE
Missing Types
If route types are missing:
- Ensure the route file exists
- Check
routes.ts configuration is valid
- Look for errors in the console
See Also