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.
npx skills add existential-birds/beagle --skill react-router-v7 -a claude-codeWhat 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>anduseFetcherfor one mutation - set up nested routes with
Outletand an index page - move data fetching out of
useEffectinto aloader - 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
createBrowserRouterfor a product catalog - Should this be
useFetcheror<Form>? The mutation stays on the same page - Rewrite this data fetch as a
loaderand add anErrorBoundary
How to install react-router-v7
1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-router-v7 -a claude-code2Manually: put the files in place
~/.claude/skills/react-router-v7/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/react-router-v7/
1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-router-v7 -a codex2Manually: put the files in place
.agents/skills/react-router-v7/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-router-v7 -a cursor2Manually: put the files in place
.cursor/skills/react-router-v7/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install existential-birds/beagle react-router-v72Manually: put the files in place
.github/skills/react-router-v7/SKILL.md1Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-router-v7 -a gemini-cli2Platform CLI
gemini skills install https://github.com/existential-birds/beagle --path plugins/beagle-react/skills/react-router-v7Manually: put the files in place
.devin/skills/react-router-v7/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add existential-birds/beagle --skill react-router-v7 -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
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