← スキル一覧に戻る
1. Route Configuration (

react-router-setup
by sethjuarez
⭐ 1🍴 0📅 2026年1月22日
SKILL.md
name: react-router-setup description: Creates new React Router v7 Framework Mode routes with proper loaders, actions, TypeScript types, error boundaries, and follows best practices for route configuration, nested routing, and data fetching patterns.
React Router Framework Mode Route Setup
This skill helps you create well-structured React Router v7 routes following Framework Mode best practices.
What This Skill Does
- Creates new route files with proper TypeScript setup
- Configures route definitions in
app/routes.ts - Sets up loaders for data fetching (server and/or client)
- Creates actions for form handling and mutations
- Implements error boundaries and loading states
- Follows type-safe patterns with auto-generated types
When to Use This Skill
Use this skill when you need to:
- Create a new route in a React Router v7 application
- Set up data loading with loaders
- Add form handling with actions
- Create nested routes with layouts
- Implement protected routes with authentication
- Set up dynamic routes with parameters
Route Creation Checklist
1. Route Configuration (app/routes.ts)
Always update the route configuration first:
import {
type RouteConfig,
route,
index,
layout,
prefix,
} from "@react-router/dev/routes";
export default [
// Index route
index("./home.tsx"),
// Simple route
route("about", "./about.tsx"),
// Dynamic route
route("products/:productId", "./products/product.tsx"),
// Nested routes
route("dashboard", "./dashboard.tsx", [
index("./dashboard/home.tsx"),
route("settings", "./dashboard/settings.tsx"),
]),
// Layout routes (no URL segment)
layout("./auth/layout.tsx", [
route("login", "./auth/login.tsx"),
route("register", "./auth/register.tsx"),
]),
// Route prefixes
...prefix("admin", [
route("users", "./admin/users.tsx"),
route("settings", "./admin/settings.tsx"),
]),
] satisfies RouteConfig;
2. Basic Route Module Template
// app/routes/[route-name].tsx
import type { Route } from "./+types/[route-name]";
// Server-side data loading
export async function loader({ params, request }: Route.LoaderArgs) {
// Fetch data from database, API, etc.
const data = await fetchData(params.id);
// Throw redirect if needed
if (!data) {
throw new Response("Not Found", { status: 404 });
}
return { data };
}
// Client-side data loading (optional)
export async function clientLoader({ params, serverLoader }: Route.ClientLoaderArgs) {
// Option 1: Client-only data
const data = await fetch(`/api/data/${params.id}`).then(r => r.json());
return data;
// Option 2: Enhance server data
const serverData = await serverLoader();
const clientData = await fetchClientData();
return { ...serverData, ...clientData };
}
// Form handling and mutations
export async function action({ request, params }: Route.ActionArgs) {
const formData = await request.formData();
const intent = formData.get("intent");
switch (intent) {
case "create":
const result = await createItem(formData);
return redirect(`/items/${result.id}`);
case "update":
await updateItem(params.id, formData);
return { success: true };
case "delete":
await deleteItem(params.id);
return redirect("/items");
default:
throw new Response("Invalid intent", { status: 400 });
}
}
// Main component
export default function Component({ loaderData }: Route.ComponentProps) {
const { data } = loaderData;
return (
<div>
<h1>{data.title}</h1>
{/* Your UI here */}
</div>
);
}
// Error boundary
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
if (error instanceof Response) {
return (
<div>
<h1>{error.status} {error.statusText}</h1>
<p>Something went wrong.</p>
</div>
);
}
return (
<div>
<h1>Error</h1>
<p>{error.message}</p>
</div>
);
}
// Hydration fallback (for clientLoader with hydrate: true)
export function HydrateFallback() {
return <div>Loading...</div>;
}
3. Layout Route Template
// app/routes/[layout-name].tsx
import { Outlet } from "react-router";
import type { Route } from "./+types/[layout-name]";
export async function loader({ request }: Route.LoaderArgs) {
// Load shared data for all child routes
const user = await getAuthenticatedUser(request);
return { user };
}
export default function Layout({ loaderData }: Route.ComponentProps) {
return (
<div>
<nav>{/* Shared navigation */}</nav>
<main>
<Outlet /> {/* Child routes render here */}
</main>
<footer>{/* Shared footer */}</footer>
</div>
);
}
4. Protected Route Pattern
// app/routes/protected.tsx
import { redirect } from "react-router";
import type { Route } from "./+types/protected";
export async function loader({ request }: Route.LoaderArgs) {
const user = await getAuthenticatedUser(request);
if (!user) {
throw redirect("/login");
}
return { user };
}
export default function ProtectedRoute({ loaderData }: Route.ComponentProps) {
return <div>Welcome, {loaderData.user.name}!</div>;
}
5. Form Handling with Progressive Enhancement
import { Form } from "react-router";
export default function FormExample() {
return (
<Form method="post">
<input type="text" name="title" required />
<input type="hidden" name="intent" value="create" />
<button type="submit">Submit</button>
</Form>
);
}
Key Best Practices
- Type Safety: Always import and use types from
./+types/[route-name] - Server-First: Prefer
loaderoverclientLoaderfor better SEO and performance - Error Handling: Always implement
ErrorBoundaryfor graceful error handling - Progressive Enhancement: Use
<Form>instead of<form>for better UX - Redirects: Use
redirect()for navigation in loaders/actions - Loading States: Add
HydrateFallbackwhen using client loaders - Validation: Validate form data in actions before processing
- HTTP Status Codes: Return proper status codes (404, 500, etc.)
Common Patterns
Optimistic UI with useFetcher
import { useFetcher } from "react-router";
function OptimisticExample() {
const fetcher = useFetcher();
const isDeleting = fetcher.state !== "idle";
return (
<fetcher.Form method="post">
<input type="hidden" name="intent" value="delete" />
<button disabled={isDeleting}>
{isDeleting ? "Deleting..." : "Delete"}
</button>
</fetcher.Form>
);
}
Prefetching for Instant Navigation
import { Link } from "react-router";
<Link to="/products/123" prefetch="intent">
View Product
</Link>
Pre-rendering Static Routes
// react-router.config.ts
import type { Config } from "@react-router/dev/config";
export default {
async prerender() {
return [
"/",
"/about",
"/contact",
// Generate from data
...(await getProductIds()).map(id => `/products/${id}`),
];
},
} satisfies Config;
File Structure
app/
├── root.tsx # Root layout
├── routes.ts # Route configuration
├── routes/
│ ├── _index.tsx # Home page
│ ├── about.tsx # /about
│ ├── dashboard.tsx # Parent route
│ ├── dashboard.home.tsx # /dashboard
│ ├── dashboard.settings.tsx # /dashboard/settings
│ └── products/
│ └── $productId.tsx # /products/:productId
└── +types/ # Auto-generated (gitignored)
Next Steps
After creating a route:
- Update
app/routes.tswith the route configuration - Create the route file with loader, action, and component
- Add TypeScript types using imports from
+types - Implement error boundary
- Add loading states if using clientLoader
- Test the route with different data scenarios
- Consider pre-rendering if content is static
スコア
総合スコア
45/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
○言語
プログラミング言語が設定されている
0/5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です