Skip to main content
skill CX Codex

react-router-v7 — Claude Code skill: React Router v7 patterns

A skill that gives your agent React Router v7 practices: when to reach for a loader, a Form or a fetcher, and how nested routes fit together.

Install into Claude Code · Skills CLI (Vercel) All methods and platforms ↓
npx skills add existential-birds/beagle --skill react-router-v7 -a claude-code
Open on GitHub

What react-router-v7 does

The skill covers data-driven routing in React Router v7. It walks through router setup with createBrowserRouter and RouterProvider, Framework Mode with the Vite plugin and a routes.ts file, nested routes rendered by Outlet, dynamic segments like teams/:teamId, optional :lang? and splats such as files/*.

A separate section sequences the decisions. The agent answers questions in order and stops at the first matching API: if the URL or history stack has to change, you get <Form> with a route action; if the mutation stays on the same route, it picks useFetcher(). The same logic applies to data — a loader when the first render needs it, useEffect when the fetch starts after mount from a user action or a timer.

There is also a comparison table of Framework, Data and Declarative modes. That one matters if your app already runs on <BrowserRouter> and you are weighing a move. Deeper material sits in references/loaders.md, references/actions.md, references/navigation.md and references/advanced.md, covering parallel loading, validation, protected routes and lazy loading.

What it helps with

  • decide between <Form> and useFetcher for one mutation
  • set up nested routes with Outlet and an index page
  • move data fetching out of useEffect into a loader
  • work out dynamic segments and splats in route paths
  • compare Framework, Data and Declarative modes before migrating

Example prompts for the agent

  • Set up routes with createBrowserRouter for a product catalog
  • Should this be useFetcher or <Form>? The mutation stays on the same page
  • Rewrite this data fetch as a loader and add an ErrorBoundary

How to install react-router-v7

1Skills CLI (Vercel)

npx skills add existential-birds/beagle --skill react-router-v7 -a claude-code

2Manually: put the files in place

~/.claude/skills/react-router-v7/SKILL.md

Copy the whole skill folder; for one project use .claude/skills/react-router-v7/

Step by step, with commands for each platform: How to install skills in Claude Code: five ways

Questions about react-router-v7

How do I install react-router-v7 in Claude?

Run npx skills add existential-birds/beagle --skill react-router-v7 -a claude-code. The skill file lands at ~/.claude/skills/react-router-v7/SKILL.md.

What does react-router-v7 do?

It hands your agent a reference for React Router v7: route configuration, data loading, mutations, navigation and protected routes. The agent consults it while you write routing code.

How is Framework Mode different from Data Mode?

Framework Mode defines routes in routes.ts through the Vite plugin, generates types automatically and includes SSR. Data Mode builds the router by hand with createBrowserRouter, so types and SSR are yours to wire up.

Source text

SKILL.md · Apache-2.0
---
name: react-router-v7
description: React Router v7 best practices for data-driven routing. Use when implementing routes, loaders, actions, Form components, fetchers, navigation guards, protected routes, or URL search params. Triggers on createBrowserRouter, RouterProvider, useLoaderData, useActionData, useFetcher, NavLink, Outlet.
---

# React Router v7 Best Practices

## Quick Reference

**Router Setup (Data Mode)**:
```tsx
import { createBrowserRouter, RouterProvider } from "react-router";

const router = createBrowserRouter([
  {
    path: "/",
    Component: Root,
    ErrorBoundary: RootErrorBoundary,
    loader: rootLoader,
    children: [
      { index: true, Component: Home },
      { path: "products/:productId", Component: Product, loader: productLoader },
    ],
  },
]);

ReactDOM.createRoot(root).render(<RouterProvider router={router} />);
```

**Framework Mode (Vite plugin)**:
```ts
// routes.ts
import { index, route } from "@react-router/dev/routes";

export default [
  index("./home.tsx"),
  route("products/:pid", "./product.tsx"),
];
```

## Route Configuration

### Nested Routes with Outlets

```tsx
createBrowserRouter([
  {
    path: "/dashboard",
    Component: Dashboard,
    children: [
      { index: true, Component: DashboardHome },
      { path: "settings", Component: Settings },
    ],
  },
]);

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Outlet /> {/* Renders child routes */}
    </div>
  );
}
```

### Dynamic Segments and Splats

```tsx
{ path: "teams/:teamId" }           // params.teamId
{ path: ":lang?/categories" }       // Optional segment
{ path: "files/*" }                 // Splat: params["*"]
```

## Key Decision Points

### Form vs Fetcher

**Use `<Form>`**: Creating/deleting with URL change, adding to history
**Use `useFetcher`**: Inline updates, list operations, popovers - no URL change

### Loader vs useEffect

**Use loader**: Data before render, server-side fetch, automatic revalidation
**Use useEffect**: Client-only data, user-interaction dependent, subscriptions

## Gates (decision sequencing)

Answer **in order**. **Pass** means the condition is true; pick the API on the same line and **stop**.

### `<Form>` vs `useFetcher`

1. **Must the URL or history stack change** (bookmark/share, back returns to prior screen)?  
   - **Pass →** `<Form>` / route `action` (or `useSubmit` + navigation). **Stop.**  
   - **Fail →** Step 2.
2. **Mutation stays on the same route** (inline edit, modal, list row, no address change)?  
   - **Pass →** `useFetcher()`. **Stop.**  
   - **Fail →** Re-check step 1; you may need a dedicated action route or POST to the current URL.

### `loader` vs `useEffect`

1. **Is data needed for correct first render** (or your intended `<Suspense>` boundary) for this route?  
   - **Pass →** `loader` (Framework: `clientLoader` when appropriate). **Stop.**  
   - **Fail →** Step 2.
2. **Fetch only after mount** from user action, timer, or subscription (not route entry)?  
   - **Pass →** `useEffect` / event handlers. **Stop.**  
   - **Fail →** Prefer loader + revalidation over an effect that mirrors navigation.

## Additional Documentation

- **Data Loading**: See [references/loaders.md](references/loaders.md) for loader patterns, parallel loading, search params
- **Mutations**: See [references/actions.md](references/actions.md) for actions, Form, fetchers, validation
- **Navigation**: See [references/navigation.md](references/navigation.md) for Link, NavLink, programmatic nav
- **Advanced**: See [references/advanced.md](references/advanced.md) for error boundaries, protected routes, lazy loading

## Mode Comparison

| Feature | Framework Mode | Data Mode | Declarative Mode |
|---------|---------------|-----------|------------------|
| Setup | Vite plugin | `createBrowserRouter` | `<BrowserRouter>` |
| Type Safety | Auto-generated types | Manual | Manual |
| SSR Support | Built-in | Manual | Limited |
| Use Case | Full-stack apps | SPAs with control | Simple/legacy |

© existential-birds · Apache-2.0 · existential-birds/beagle

Similar skills

Everything for Frontend and UI →

From the same set8

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor