Agent skills, system prompts, and AI developer rules for remix-run/react-router
# React Router Development Guide
## Commands
- **Build**: `pnpm build` (all packages) or `pnpm run --filter <package> build` (single package)
- **Test (Jest)**: `pnpm test` (all packages), `pnpm test packages/<package>/` (single package), `pnpm test packages/react-router/__tests__/router/fetchers-test.ts` (single file), or `pnpm test -- -t "action fetch"` (tests matching name)
- **Integration tests (Playwright)**: `pnpm test:integration --project chromium` (build + test all), `pnpm test:integration:run --project chromium` (test only, all), `pnpm test:integration:run integration/middleware-test.ts --project chromium` (single file), or `pnpm test:integration:run --project chromium -g "middleware"` (tests matching name)
- **Typecheck**: `pnpm run typecheck`
- **Lint**: `pnpm run lint`
- **Docs generation**: `pnpm run docs` (regenerates API docs from JSDoc)
- **Type generation**: `pnpm run typegen` (Framework Mode only)
- **Clean**: `pnpm run clean` (git clean -fdX)
## Modes
**Five distinct modes**: Declarative, Data, Framework, RSC Data (unstable), RSC Framework (unstable). **Always identify which mode(s) a feature applies to.**
1. **Declarative**: `<BrowserRouter>`, `<Routes>`, `<Route>`
2. **Data**: `createBrowserRouter()` with `loader`/`action`, `<RouterProvider>`
3. **Framework**: Vite plugin + `routes.ts` + Route Module API (route exports like `loader`, `action`, `default`) + type generation + SSR/SPA
4. **RSC Data** (unstable): RSC runtime APIs, manual bundler setup, runtime route config
5. **RSC Framework** (unstable): Framework Mode with `unstable_reactRouterRSC` Vite plugin
**RSC mode differences:**
- **RSC Framework**: `unstable_reactRouterRSC` plugin, `@vitejs/plugin-rsc`, different entry points/format
- **RSC Data**: Manual bundler, runtime route config typically in `src/routes.ts`, `unstable_RSCRouteConfig`, different runtime APIs, `setupRscTest` in `integration/rsc/`
## Architecture
- **Monorepo**: pnpm workspace, packages in `packages/`
- **Key packages**:
- `react-router`: Core (all modes) - `lib/components.tsx`, `lib/hooks.tsx`, `lib/router/`, `lib/dom/`, `lib/rsc/`
- `@react-router/dev`: Framework tooling - `vite/plugin.ts` (Framework), `vite/rsc/plugin.ts` (RSC Framework), `typegen/`
- `@react-router/node`, `@react-router/cloudflare`, `@react-router/express`: Server adapters
- `@react-router/serve`: Minimal server for Framework Mode
- `@react-router/fs-routes`: File-system routing (`flatRoutes()`)
## Testing
### Unit Tests (`packages/react-router/__tests__/`)
Use Jest for pure routing logic, pure server runtime behavior, router state, React component behavior. No build required.
```bash
pnpm test # All packages
pnpm test packages/react-router/ # Single package
pnpm test packages/react-router/__tests__/router/fetchers-test.ts # Single file
pnpm test -- -t "action fetch" # Tests matching name
```
### Integration Tests (`integration/`)
Use Playwright for Vite plugin, build pipeline, SSR/hydration, RSC, type generation.
```bash
pnpm test:integration --project chromium # Build + test all
pnpm test:integration:run --project chromium # Test only, all
pnpm test:integration:run integration/middleware-test.ts --project chromium # Single file
pnpm test:integration:run --project chromium -g "middleware" # Tests matching name
```
**Project**: Always use `chromium` for integration tests, unless explicitly stated otherwise.
**Rebuild when**: First run, after changing `packages/` (not needed for test-only changes)
**Organization**: Use `createFixture()` โ `createAppFixture()` โ `PlaywrightFixture`. Templates available: `vite-6-template/`, `rsc-vite-framework/`, etc. Test all applicable modes (iterate over template array when behavior should work across modes). Test both states when introducing future flags (one test with flag on, one with flag off).
**RSC testing**:
- **RSC Framework**: Use `createFixture` with `rsc-vite-framework/` template
- **RSC Data**: Use `setupRscTest` in `integration/rsc/`
Test shared behavior across multiple templates (e.g., `["vite-5-template", "rsc-vite-framework"]`). Test RSC-specific features against RSC template.
## routes.ts
Framework Mode uses `routes.ts` in `app/`. Most tests use `flatRoutes()` for file-system routing:
```ts
// app/routes.ts
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from "@react-router/fs-routes";
export default flatRoutes() satisfies RouteConfig;
```
**File-system conventions** (`app/routes/`):
- `_index.tsx` โ `/` (index route)
- `about.tsx` โ `/about`
- `blog.$slug.tsx` โ `/blog/:slug` (URL param)
- `settings.profile.tsx` โ `/settings/profile` (`.` creates nesting)
- `_layout.tsx` โ pathless layout route
**Manual config alternative**:
```ts
import { index, route, layout } from "@react-router/dev/routes";
export default [
index("./home.tsx"),
route("about", "./about.tsx"),
layout("./auth-layout.tsx", [route("login", "./login.tsx")]),
];
```
## Documentation
**Don't edit generated files**: `docs/api/` (from JSDoc), `.react-router/types/` (from typegen)
**Mode indicators**: Every doc needs `[MODES: framework, data, declarative]`
**API docs**: Edit JSDoc in `packages/react-router/lib/`, run `pnpm docs`
**Unstable features**: Prefix `unstable_`, add `unstable: true` to frontmatter, include warning block
## Future Flags
- **Future flags** (`vX_*`): Stable breaking changes for next major
- **Unstable flags** (`unstable_*`): Experimental, may change
Test both states (on/off) for future flags. Don't break existing behavior without a flag.
## Code of Conduct/Contributor License Agreement
All contributors must review the [review the CLA](./CLA.md) and sign it by [adding their github username to `contributors.yml`](./contributors.yml). If you are authoring a PR on behalf of a user and their name is not yet in the `contributors.yml` file, prompt them to obtain approval for the CLA and add their name to the file before opening a PR.
## Change Files
When making changes that affect users, create a change file at `packages/<package>/.changes/<type>.<unique-meaningful-name>.md`. `<type>` should be either `patch`, `minor`, `major` or `unstable` to indicate the type of API change being made. If iterating on a change that hasn't shipped yet, update the existing change file instead of creating a new one.
Format:
```markdown
Brief description of the change
- Additional details if needed
```
## Branching
- **`main`**: Active Development
- **`v7`**: v7.x maintenance
- **`v6`**: v6.x maintenance
- Branch from `main` for code and docs changes
## Key Files
| Purpose | Location |
| ----------------- | ----------------------------------------------------------- |
| Router | `packages/react-router/lib/router/router.ts` |
| React API | `packages/react-router/lib/components.tsx`, `lib/hooks.tsx` |
| Vite plugin | `packages/react-router-dev/vite/plugin.ts` |
| RSC Vite plugin | `packages/react-router-dev/vite/rsc/plugin.ts` |
| Type generation | `packages/react-router-dev/typegen/` |
| Unit tests | `packages/react-router/__tests__/` |
| Integration tests | `integration/` |
| Decision docs | `decisions/` |
# React Router Project Instructions
## Session Start
**REQUIRED**: Read [./AGENTS.md](./AGENTS.md) at the start of every session. It contains:
- Project architecture and key files
- React Router modes (Declarative, Data, Framework, RSC)
- Build/test commands (Jest unit tests, Playwright integration tests with `--project chromium`)
- Testing patterns and conventions
- Documentation guidelines
### Skills
If there is an [`.agents/skills`](.agents/skills) directory in the repository, symlink those skills to `.claude/skills` to make sure they are made available to Claude. Refresh you list of available skills if needed. This is a git ignored directory because we want to keep the canonical skills in `.agents/skills`.
## During Work
**Always consult AGENTS.md** when you need to:
- Run tests or builds
- Understand which mode(s) a feature applies to
- Find key file locations
- Understand testing patterns
Do not guess at commands - reference AGENTS.md for the correct syntax.
---
name: create-pr
description: Create and package React Router pull requests. Use when the user asks to create, open, prepare, or finish a PR for this repository, including branch/commit/push handoff, draft PR creation, PR body writing, and applying GitHub labels such as pkg:*, feat:*, docs, github-actions, or dependencies.
---
# Create React Router PR
Create the pull request handoff for completed React Router work. Default to a draft PR targeting `main` unless the user explicitly asks for a ready PR or a different base branch.
## Preconditions
- Inspect `git status --short --branch` and `git branch --show-current`.
- Do not include unrelated dirty files. If unrelated changes are present, leave them unstaged and mention them.
- If the worktree is detached, create a branch from the current `HEAD` before committing. Use the branch name requested by the user, an existing repository convention, or `<author>/<semantic-name>` when no stronger convention is available.
- If already on a suitable named branch, use it.
- Confirm appropriate automated coverage exists or was added. Do not list routine CI-covered checks in the PR body.
- If user-facing functionality is being updated, check that the appropriate package has a change file under `packages/<package>/.changes/`. This is not necessary for docs-only, GitHub Actions/workflow-only, or dependency-maintenance PRs unless the dependency change itself has user-facing impact.
## Context
- Capture what changed, why it changed, and who it affects.
- Find related issues, discussions, or PRs and include links when relevant.
- Prefer `git diff --stat` plus focused `git diff` over broad repo archaeology when the change is small.
- If the user supplies a report, issue, discussion, or related PR, treat that as the primary context source.
- For new feature work, include a concise usage snippet. If the feature replaces or improves an older approach, include before/after examples when they help reviewers.
## Commit and Push
1. Review the diff with `git diff --stat` and focused `git diff` as needed.
2. Stage only the intended files.
3. Commit with a concise imperative subject.
4. Push the branch before creating the PR.
## PR Creation
Save the PR body to a temporary file, then use `gh pr create` with:
```sh
gh pr create --draft --base main --head <branch> --title "<title>" --body-file <file>
```
- Omit `--draft` only when the user explicitly asks for a ready PR.
- Change `--base` only when the user explicitly requests a different base branch.
- Keep shell quoting simple. Prefer a body file if the body contains backticks, quotes, or multiple paragraphs.
- Include issue/discussion links when known. Use `Closes #NNNN` for bug fixes the PR should close; use `Implements #NNNN` or a plain link for RFCs/discussions when closing semantics are not appropriate.
- Include testing notes only for manual checks, unusual verification, skipped non-CI checks, or failures that reviewers should know about.
- If `gh pr create` fails, leave the branch pushed when possible and give the user a ready-to-open compare URL plus the prepared title and body.
Recommended PR body shape:
````markdown
This change ...
- Optional extra detail when useful.
```tsx
// Optional feature usage example
```
````
```tsx
// Optional before/after example
```
**Testing**
- Optional manual or non-CI verification notes only.
````
- Do not use a `## Summary` heading. Start with one or two short sentences explaining what the change accomplishes.
- Add bullets after the opening only when more detail is useful.
- Include usage examples for new features, and before/after examples for replacements or improvements, when they help reviewers understand the change.
- Add `**Testing**` with bullets below the description only when there are manual checks, unusual verification steps, skipped non-CI checks, or failures reviewers should know about. Omit it for routine automated coverage.
## Testing Notes
- Do not list linting, typechecking, unit test, or integration test commands in the PR body, even if they were run locally. CI runs these automatically.
- Do not say that CI will run routine linting, typechecking, unit tests, or integration tests. That is assumed for every PR.
- Do not add manual testing instructions by default.
- Add manual testing instructions only when necessary, such as visual/UI behavior that needs human review, environment-specific behavior not covered by CI, release/publish dry-run steps, external service integration, or a reproduction that cannot be expressed reliably in automated tests.
- When manual testing is necessary, keep the instructions minimal and directly tied to the uncovered risk.
## Labels
Apply labels after the PR exists. Rely on the stable labels listed in this skill for normal PRs.
Apply labels with:
```sh
gh pr edit <number-or-url> --add-label "<label>"
````
If `gh pr edit --add-label` fails because the specified label is invalid or missing, run:
```sh
gh label list --limit 200
```
Then choose the correct label from the live list and update this skill in place so the stable label guidance stays current. Use real labels only. If the right label does not exist, do not invent one; mention the missing label.
### Package Labels
Add every applicable `pkg:*` label based on touched package paths:
| Touched path | Label |
| ---------------------------------------------------- | ----------------------------------------------- |
| `packages/react-router/` | `pkg:react-router` |
| `packages/react-router-dev/` | `pkg:@react-router/dev` |
| `packages/create-react-router/` | `pkg:create-react-router` |
| `packages/react-router-architect/` | `pkg:@react-router/architect` |
| `packages/react-router-cloudflare/` | `pkg:@react-router/cloudflare` |
| `packages/react-router-node/` | `pkg:@react-router/node` |
| `packages/react-router-serve/` | `pkg:@react-router/serve` |
| `packages/react-router-express/` | `pkg:@react-router/express` |
| `packages/react-router-fs-routes/` | `pkg:@react-router/fs-routes` |
| `packages/react-router-remix-routes-option-adapter/` | `pkg:@react-router/remix-routes-option-adapter` |
If a package path is unclear, inspect its `package.json` `name` and use `pkg:<name>` when that label exists. If a change touches generated artifacts or integration tests only, infer the package label from the runtime/tooling area being tested. For example, Vite plugin or prerender integration coverage usually maps to `pkg:@react-router/dev`.
### Feature Labels
Add applicable `feat:*` labels for the behavior area being changed. Common labels include:
| Behavior area | Label |
| --------------------------------------------------------------------------------- | --------------------------- |
| Core navigation, loaders/actions, fetchers, redirects, matching, and router state | `feat:router` |
| Route config APIs and `routes.ts` | `feat:routes.ts` |
| Vite plugin and build pipeline behavior | `feat:vite` |
| SPA mode | `feat:spa-mode` |
| Prerendering | `feat:prerender` |
| Lazy route discovery | `feat:lazy-route-discovery` |
| Hydration and hydration fallback behavior | `feat:hydration` |
| View transition APIs | `feat:view-transitions` |
| Middleware behavior | `feat:middleware` |
| Split route module behavior | `feat:split-route-modules` |
| Streaming behavior | `feat:streaming` |
| CSS handling | `feat:css` |
| Windows-specific fixes | `feat:windows` |
| RSC Data or RSC Framework behavior | `feat:rsc` |
| Path matching semantics | `feat:path-matching` |
| Single fetch behavior | `feat:single-fetch` |
| Types, typegen, and TypeScript behavior | `feat:typescript` |
Multiple feature labels are fine when the diff truly spans multiple areas. Prefer the most specific label that exists.
### Non-Package Labels
Some PRs do not need package or feature labels:
- Add `docs` for documentation-only changes.
- Add `github-actions` for `.github/workflows/` or Actions infrastructure changes.
- Add `dependencies` for dependency or lockfile-only maintenance.
- Add version labels such as `v6`, `v7`, or `v8` only when the PR is intentionally scoped to that release line or the user asks for it.
## Final Report
Report:
- Branch name.
- Commit hash.
- PR URL and whether it is draft or ready.
- Base branch.
- Labels applied.
- Verification performed or skipped.
# Data Mode
Data Mode uses data routers such as `createBrowserRouter` and renders with `<RouterProvider>`. It gives an app route objects, loaders, actions, pending UI, fetchers, and SSR primitives without adopting the Framework Vite plugin or route-module file conventions.
Use this reference after the main skill identifies a Data Mode app.
## Read the Local Docs by Mode
Start with:
```txt
react-router/docs/start/modes.md
react-router/docs/start/data/index.md
```
Then use the Data docs under:
```txt
react-router/docs/start/data/
```
Those files cover installation, route objects, routing, data loading, actions, navigation, pending UI, and testing. For task-specific details, read relevant files in:
```txt
react-router/docs/how-to/
react-router/docs/explanation/
```
Always check the `[MODES: data, ...]` marker in a doc before applying it.
## Data Router Shape
Typical setup:
```tsx
import { createBrowserRouter, RouterProvider } from "react-router";
const router = createBrowserRouter([
{
path: "/",
Component: Root,
loader: rootLoader,
children: [
{ index: true, Component: Home },
{
path: "projects/:projectId",
Component: Project,
loader: projectLoader,
},
],
},
]);
root.render(<RouterProvider router={router} />);
```
Look for route object arrays and APIs such as:
- `createBrowserRouter`
- `createHashRouter`
- `createMemoryRouter`
- `RouterProvider`
- `loader`
- `action`
- `Component`
- `ErrorBoundary`
- `lazy`
- `children`
## Route Objects and Routing
Before editing route configuration, read:
```txt
react-router/docs/start/data/routing.md
react-router/docs/start/data/route-object.md
```
Rules:
- Keep route objects outside render when possible.
- Use nested routes for shared layouts and data boundaries.
- Use index routes for default child content.
- Use dynamic segments and splats according to route-object docs.
- Prefer `Component`/`ErrorBoundary` route object properties in Data Mode examples unless the existing app uses `element` consistently.
## Data and Mutations
Before working on data loading or mutations, read:
```txt
react-router/docs/start/data/data-loading.md
react-router/docs/start/data/actions.md
```
Rules:
- Load route data with route `loader` functions.
- Mutate route data with route `action` functions.
- Prefer loaders/actions over route-level `useEffect` fetching.
- Use `request`, `params`, and returned/throwable Responses as described in the docs.
- Let React Router revalidate after actions unless there is a documented reason to customize revalidation.
Common patterns:
- Validation failure from an action: return `data({ errors, values }, { status: 400 })`, then render errors with `useActionData()` or `fetcher.data`.
- Missing record in a loader: throw `data("Not Found", { status: 404 })` and render the route `ErrorBoundary`.
- Search/filter data: parse `new URL(request.url).searchParams` in the loader so the URL is shareable and bookmarkable.
## Forms, Fetchers, and Pending UI
For forms and pending UI, read:
```txt
react-router/docs/start/data/actions.md
react-router/docs/start/data/pending-ui.md
react-router/docs/how-to/fetchers.md
react-router/docs/explanation/form-vs-fetcher.md
```
Rules of thumb:
- Search/filter form that updates the URL: `<Form method="get">`.
- Mutation that should change URL/history or redirect after completion: `<Form method="post">`.
- Mutation that should keep the user on the same page: `useFetcher` / `<fetcher.Form>`.
- Optimistic UI: derive from `fetcher.formData` or `navigation.formData`.
## Navigation and URL State
Before changing navigation or search params, read:
```txt
react-router/docs/start/data/navigating.md
react-router/docs/how-to/search-params.md
react-router/docs/explanation/location.md
```
Rules:
- Use `<Link>`/`<NavLink>` for user-initiated internal navigation.
- Use `redirect` in loaders/actions when navigation follows data loading or mutations.
- Use `useNavigate` for imperative client-side event navigation.
- Treat URL params as strings and validate/parse them.
- Preserve unrelated search params unless intentionally resetting them.
## SSR in Data Mode
Data Mode SSR is manual and lower-level than Framework Mode. Before implementing or changing SSR, read the Data Mode custom/SSR docs and match existing server abstractions.
Start with:
```txt
react-router/docs/start/data/custom.md
```
Look for APIs like `createStaticHandler`, `createStaticRouter`, `StaticRouterProvider`, and hydration data handling in the current app before changing anything.
## RSC Data
If this Data Mode app uses `unstable_RSCRouteConfig`, RSC route config, or low-level RSC server APIs, also read:
```txt
references/rsc.md
react-router/docs/how-to/react-server-components.md
```
# React Server Components (RSC)
React Router's RSC support is unstable and exists in two variants:
- **RSC Framework Mode**: Framework Mode with the unstable RSC Vite plugin.
- **RSC Data Mode**: lower-level RSC runtime APIs and manual bundler/server integration.
Use this reference in addition to `framework-mode.md` or `data-mode.md` after the main skill identifies an RSC app.
## Read the Local RSC Docs
Start with:
```txt
react-router/docs/how-to/react-server-components.md
```
Then read the relevant base mode docs:
```txt
react-router/docs/start/framework/
react-router/docs/start/data/
```
RSC docs may describe differences from non-RSC mode rather than repeating every Framework/Data concept, so keep both layers in mind.
## Detect RSC Framework Mode
Look for:
- `unstable_reactRouterRSC` imported from `@react-router/dev/vite`
- `@vitejs/plugin-rsc`
- `vite.config.ts` with `plugins: [reactRouterRSC(), rsc()]`
- Framework route modules plus RSC route exports
- RSC entry files such as `entry.rsc`
RSC Framework Mode uses a different Vite plugin from non-RSC Framework Mode. Do not swap it for the regular `reactRouter()` plugin.
## Detect RSC Data Mode
Look for:
- `unstable_RSCRouteConfig`
- route config passed to lower-level RSC APIs
- APIs such as `unstable_matchRSCServerRequest`, `unstable_routeRSCServerRequest`, `unstable_RSCHydratedRouter`, or `unstable_RSCStaticRouter`
- custom bundler/server setup around RSC
RSC Data Mode is more manual than RSC Framework Mode. Match the app's bundler and server abstractions before changing routes or entries.
## RSC Route Module Differences
In RSC Framework Mode, many normal Framework Mode concepts still apply, but routes can use server component exports.
Important route-module concepts from the RSC docs include:
- `ServerComponent` instead of the usual client `default` component
- `ServerErrorBoundary` paired with `ErrorBoundary`
- `ServerLayout` paired with `Layout`
- `ServerHydrateFallback` paired with `HydrateFallback`
- server-rendered React elements returned from loaders/actions
A route module cannot export both the normal client component and its server component counterpart for the same role. Read the RSC docs before adding these exports.
## Client/Server Boundaries
RSC code must respect React's client/server split:
- Use `"use client"` for components that need hooks, browser APIs, or event handlers.
- Use server-only modules for server data access and secrets.
- In RSC Framework Mode, prefer the `server-only` and `client-only` boundary imports described in the docs.
- Do not assume `.server`/`.client` file naming works the same way in RSC Framework Mode; read the RSC docs before relying on those conventions.
## Data Loading in RSC
RSC changes where data can be loaded:
- Server Components can fetch data directly on the server.
- Loaders/actions may still exist and can have RSC-specific behavior.
- Client components still need client-safe data and cannot directly access server-only modules.
When choosing between a server component fetch, a loader, and a client loader/action, follow the RSC docs and match existing app patterns.
## Stability
RSC APIs are explicitly unstable. Before implementing or refactoring RSC code:
- Check the installed React Router version.
- Check the installed `@vitejs/plugin-rsc` version.
- Read the app's existing RSC entry/config files.
- Prefer minimal changes that match current patterns.
---
name: react-router
description: Build applications with React Router in Framework, Data, Declarative, and unstable RSC modes. Use when configuring routes, route modules, loaders, actions, forms, fetchers, navigation, pending UI, SSR/SPA/pre-rendering, middleware, URL params/search params, or React Router upgrades.
license: MIT
---
# React Router
React Router is mode-specific. Before changing an app, identify the mode, load the matching reference, then read the installed docs for the installed package version.
## Identify the Mode
Do not apply Framework/Data patterns to a Declarative app unless you are intentionally migrating modes.
### Framework Mode
Use Framework Mode guidance when you see:
- `@react-router/dev` in dependencies
- `react-router.config.ts`
- `app/routes.ts`
- `app/entry.server.tsx` and/or `app/entry.client.tsx` files
- route modules under `app/routes/`
- route exports like `loader`, `action`, `clientLoader`, `clientAction`, `ErrorBoundary`, `meta`, `links`, or `headers`
- imports from `./+types/...`
- the React Router Vite plugin from `@react-router/dev/vite`
Framework examples usually use the default `app/` directory, but check `react-router.config.ts` for a custom `appDirectory` before assuming exact paths.
Then read `references/framework-mode.md`.
### Data Mode
Use Data Mode guidance when you see:
- `createBrowserRouter`, `createHashRouter`, `createMemoryRouter`, or `createStaticRouter`
- `<RouterProvider router={router}>`
- route objects with properties like `path`, `children`, `loader`, `action`, `Component`, `ErrorBoundary`, or `lazy`
- data APIs without the Framework Vite plugin
Then read `references/data-mode.md`.
### Declarative Mode
Use Declarative Mode guidance when you see:
- `<BrowserRouter>`, `<HashRouter>`, or `<MemoryRouter>`
- `<Routes>` and `<Route>` JSX route configuration
- route components passed with `element={<Component />}`
- no data router, no route module convention, and no loaders/actions
Then read `references/declarative-mode.md`.
### RSC Framework and RSC Data Modes
React Server Components support is unstable and exists in both Framework and Data variants. Use RSC guidance when you see:
- `unstable_reactRouterRSC`
- `@vitejs/plugin-rsc`
- `unstable_RSCRouteConfig`
- RSC entry files such as `entry.rsc`
- `ServerComponent`, `ServerErrorBoundary`, `ServerLayout`, or `ServerHydrateFallback`
- React directives or boundary packages such as `"use client"`, `"server-only"`, or `"client-only"`
For RSC Framework, read both `references/framework-mode.md` and `references/rsc.md`.
For RSC Data, read both `references/data-mode.md` and `references/rsc.md`.
## Use Installed Docs as Source of Truth
React Router ships markdown docs in the package so guidance can match the installed version:
```txt
node_modules/react-router/docs/
```
Key docs paths:
```txt
node_modules/react-router/docs/index.md
node_modules/react-router/docs/start/
node_modules/react-router/docs/how-to/
node_modules/react-router/docs/explanation/
node_modules/react-router/docs/upgrading/
```
When this skill references `react-router/docs/...`, read the matching file under `node_modules/react-router/docs/`. If the installed version does not include local docs, use the repo `docs/` directory when working inside the React Router repository; in a consuming app, fall back to version-matched website docs.
Most docs include a mode marker near the top:
```txt
[MODES: framework, data, declarative]
```
Only apply a doc when its mode marker matches the app mode. If a task spans modes, prefer the section or file that matches the current app.
RSC is documented primarily in:
```txt
node_modules/react-router/docs/how-to/react-server-components.md
```
## Skill References
Load the relevant reference after identifying the mode:
| Reference | Use When |
| -------------------------------- | --------------------------------------------- |
| `references/framework-mode.md` | Framework Mode or RSC Framework base behavior |
| `references/data-mode.md` | Data Mode or RSC Data base behavior |
| `references/declarative-mode.md` | Declarative Mode |
| `references/rsc.md` | Any unstable RSC app |
## Mode Migration Doc Index
If the user explicitly asks to switch modes, read the target mode reference plus the migration-relevant docs:
| Migration | Docs to read |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Declarative โ Data | `react-router/docs/start/modes.md`, `react-router/docs/start/data/routing.md`, `react-router/docs/start/data/data-loading.md`, `react-router/docs/start/data/actions.md` |
| Declarative/Data โ Framework | `react-router/docs/start/modes.md`, `react-router/docs/start/framework/routing.md`, `react-router/docs/start/framework/route-module.md`, `react-router/docs/how-to/route-module-type-safety.md` |
| Framework SPA/SSR/pre-render changes | `react-router/docs/start/framework/rendering.md`, `react-router/docs/how-to/spa.md`, `react-router/docs/how-to/pre-rendering.md`, `react-router/docs/start/framework/data-loading.md`, `react-router/docs/start/framework/actions.md` |
| Future flags/upgrades | `react-router/docs/upgrading/future.md` and relevant files under `react-router/docs/upgrading/` |
# What's Changed Reference
Load this when deciding whether a React Router release needs `scripts/changes/whats-changed.md`.
## Include A Manual Section For
- Major releases with baseline changes, removed packages, removed deprecated APIs, new runtime requirements, or migration guidance
- Stabilizations and flag renames where adopters of unstable APIs need explicit before/after guidance
- New APIs or flags that deserve a narrative introduction or usage example
- Cross-cutting performance or behavior work where several bullets form one user-facing story
- Breaking bug fixes, deployment-sensitive fixes, or adapter/runtime behavior changes where users may need to test or update config
## Usually Do Not Include One For
- Straightforward bug fixes
- Dependency removals or upgrades where the bullet is sufficient
- Internal refactors with no public API change
- A minor feature that is fully understandable from one concise bullet and nested detail bullets
- Releases where the only content is a normal patch list
## Existing Changelog Patterns
- `v8.1.0`: Uses headings for agent skill installation and observability metadata, including an instrumentation code example. The generated minor bullets still carry the per-package details.
- `v8.0.0`: Uses long-form migration notes for a major release: baseline support, adopted future flags, removed packages/APIs, and behavior changes.
- `v7.18.0`: Explains a CSRF check fix that may be a breaking bug fix for reverse-proxy deployments and tells users what to test.
- `v7.15.0`: Groups several unstable-to-stable API renames and route matching optimizations into narrative sections before listing individual minor bullets.
- `v7.15.1`: Uses a "What's New" style section for an unstable hook with a code example. For new release notes, prefer the current `What's Changed` file path; the generated heading will be `### What's Changed`.
## Drafting Tips
- Use `####` headings inside `scripts/changes/whats-changed.md`; the release script wraps the file in `### What's Changed` if needed
- Mention applicable React Router modes when the distinction matters: Declarative, Data, Framework, RSC Data, or RSC Framework
- Keep examples short and directly tied to adoption
- Avoid repeating the package prefix and PR-link details already provided by generated change sections
- If unsure, run `pnpm changes:preview` with and without the manual section and keep it only if the generated notes are meaningfully clearer with the narrative
---
name: implement-rfc
description: Implement a React Router RFC from a GitHub discussion URL. Fetches the proposal, evaluates community feedback, resolves outstanding questions interactively, then implements the feature with tests, future flags (if breaking), and a changeset.
disable-model-invocation: true
---
# Implement React Router RFC
Implement the RFC from the following GitHub discussion: $ARGUMENTS
## Branching
RFC implementations should start from a clean working tree. If there are uncommitted changes, stop and ask me to resolve them before continuing.
- If you are already on a named branch that is at the same HEAD as `main`, use that branch.
- Otherwise, create a branch from `main` using the format `{author}/rfc-{semantic-name}`:
```sh
git branch {author}/rfc-{semantic-name} main
git checkout {author}/rfc-{semantic-name}
```
## Workflow
### 1. Fetch and Understand the RFC
Use `WebFetch` to read the discussion URL. If a GitHub discussion number is given instead of a URL, construct:
`https://github.com/remix-run/react-router/discussions/<number>`
Extract:
- **Problem being solved**: what pain point does this RFC address?
- **Proposed API**: exact function signatures, hook names, types, option shapes
- **Affected modes**: Declarative / Data / Framework / RSC Data / RSC Framework
- **Breaking changes**: does this change or remove existing public API?
- **Open questions**: anything explicitly marked as unresolved, "TBD", or asked as a question in the proposal
- **Status**: are there linked tracking issues? Look for links to other github issues and read them to see if there is additional context.
```sh
gh issue view <number> --repo remix-run/react-router
```
### 2. Evaluate Community Feedback
Fetch all comments from the discussion:
Use `WebFetch` on `https://github.com/remix-run/react-router/discussions/<number>` and scroll through the full thread. Look for:
- **Concerns or objections** raised by community members or maintainers
- **Alternative proposals** or API shape suggestions
- **Edge cases** raised that the proposal does not address
- **Positive signals** โ repeated praise for a specific approach signals it's the right direction
- **Maintainer responses** โ Ryan Florence, Michael Jackson, or other core team members clarifying intent
Summarize the community sentiment into:
- Points of consensus (safe to proceed)
- Points of contention (need resolution before implementing)
- Unanswered questions from the original proposal
### 3. Resolve Outstanding Questions
Before writing any code, present me with a numbered list of every unresolved question โ from both the RFC itself and from community feedback. For each question:
- State the question clearly
- Summarize relevant community input
- Offer a recommended answer with reasoning
Ask me to confirm, override, or skip each question. Do not proceed to implementation until all questions are either answered or explicitly deferred.
Example format:
```
## Unresolved Questions
1. **Should `useRouterState()` accept a path argument for scoped matching?**
Community feedback: 3 comments in favor, 1 against (concerns about complexity).
Recommendation: Yes โ scoped matching improves type safety for nested routes.
โ Your decision: [confirm / override / defer]
2. **What should happen when the path doesn't match the current location?**
Recommendation: Return `null` for active state (consistent with `useMatch()`).
โ Your decision: [confirm / override / defer]
```
Save the resolved decisions to a scratch file at `tasks/rfc-decisions.md` for reference during implementation.
### 4. Plan the Implementation
Before writing code, produce a concise implementation plan covering:
- New types/interfaces to add
- New functions/hooks to implement and their file locations
- Existing APIs to deprecate (mark with `@deprecated` JSDoc + console warning in dev)
- Whether a future flag is needed (see ยง5 below)
- Test files to create or extend (unit and/or integration)
- Changeset bump level (`minor` for new features, `major` for breaking changes behind a future flag that is now defaulted on)
Present the plan to me and wait for approval before implementing.
### 5. Future Flags for Breaking Changes
If the RFC changes or removes existing public API behavior, it **must** ship behind a future flag which will start with an `unstable_` prefix.
**Future flag pattern:**
1. Add the flag to `FutureConfig` in `packages/react-router/lib/router/utils.ts`:
```ts
export interface FutureConfig {
// existing flags...
unstable_myNewBehavior: boolean;
}
```
2. Gate the new behavior on the flag:
```ts
if (router.future.unstable_myNewBehavior) {
// new behavior
} else {
// legacy behavior
}
```
3. Document the flag in `docs/upgrading/future-flags.md` if it exists.
New additive APIs (no behavior change to existing code) do **not** need a future flag.
### 6. Key File Locations
| Area | Files |
| --------------------- | -------------------------------------------- |
| Core router logic | `packages/react-router/lib/router/router.ts` |
| Router types/utils | `packages/react-router/lib/router/utils.ts` |
| React components | `packages/react-router/lib/components.tsx` |
| React hooks | `packages/react-router/lib/hooks.tsx` |
| Public exports | `packages/react-router/index.ts` |
| DOM utilities | `packages/react-router/lib/dom/` |
| Framework/Vite plugin | `packages/react-router-dev/vite/plugin.ts` |
| RSC runtime | `packages/react-router/lib/rsc/` |
| Unit tests | `packages/react-router/__tests__/` |
| Integration tests | `integration/` |
| Future flags doc | `docs/upgrading/future-flags.md` |
Confirm existing patterns before writing new code - prefer using the LSP but `Grep`/`Glob` also work. Match naming conventions and code style exactly.
### 7. Implement the Feature
Follow the approved plan. For each logical unit of work:
1. Write the implementation
2. Export from the appropriate public entry point (`packages/react-router/index.ts`)
3. Add `@deprecated` JSDoc to any APIs being superseded
4. Run typecheck to catch type errors early:
```sh
pnpm typecheck
```
Keep changes minimal and focused. Do not refactor unrelated code. Commit as often as needed.
### 8. Write Tests
**Unit tests** (for hooks, pure router logic, component behavior โ no build):
- Location: `packages/react-router/__tests__/`
- Runner: Jest โ `pnpm test packages/react-router/__tests__/<file>`
- Cover: happy path, edge cases identified in RFC/community feedback, future flag gating (if applicable), deprecation warnings
**Integration tests** (for Vite/Framework Mode, SSR, hydration):
- Location: `integration/`
- Runner: Playwright โ `pnpm test:integration:run --project chromium integration/<file>`
- Required if the RFC touches Framework Mode, file-system routing, or SSR behavior
Run all tests and confirm they pass:
```sh
pnpm test packages/react-router/
pnpm test:integration:run --project chromium # only if integration tests were added/changed
```
### 9. Lint and Typecheck
```sh
pnpm lint
pnpm typecheck
```
Fix all errors before proceeding.
### 10. Create a Change File
Create `packages/<package>/.changes/<bump-level>.<descriptive-name>.md`. Use the RFC title or tracking issue as the description:
```markdown
feat: <brief description matching the RFC title>
Implements the `useRouterState()` RFC (#12358). Deprecates `useLocation`,
`useParams`, `useSearchParams`, `useNavigation`, `useMatches`, `useMatch`,
`useNavigationType`, and `useViewTransitionState` in favor of a unified API.
Enable the `unstable_consolidatedRouterState` future flag to opt in.
```
Bump levels:
- `patch` โ bug-adjacent fix only
- `minor` โ new additive API (no breaking changes)
- `major` โ breaking change (should be rare; most breaking changes go behind a future flag as `minor` first)
- `unstable` โ new API that is not yet stable (e.g. added in a future flag, or an experimental API that may be removed without a major bump)
### 11. Report and Review
Summarize:
- What RFC was implemented and which decisions were made
- New public APIs added (with brief usage example)
- APIs deprecated and the migration path
- Future flag name (if applicable) and how to opt in
- Test coverage added
- Anything deferred or explicitly out of scope
Ask me to review and iterate before opening a PR.
### 12. Commit
Once I approve, commit and open a PR to `main`:
```sh
gh pr create --base main --title "feat: <RFC title>" --body "..."
```
PR body should include:
- Link to the RFC discussion (Closes or Implements #NNNN)
- Summary of what was implemented
- Future flag instructions if applicable
- Testing notes
- Any decisions that deviated from the original proposal and why
---
name: finish-line
description: Bring a blocked React Router community pull request across the finish line. Use when the user invokes `/finish-line` or `$finish-line`, provides a PR number or URL, and asks Codex to resolve merge blockers such as an unsigned CLA, missing change file, missing documentation, or stale contributor follow-up. Handles deciding whether to push small maintainer fixes onto the contributor PR branch or recreate the PR from main under a maintainer branch when the contributor's CLA is not signed.
---
# Finish Line
## Overview
Finish blocked community PRs in `remix-run/react-router` while respecting contributor ownership, CLA constraints, and the repo's PR packaging conventions.
Treat the PR number or URL in `$ARGUMENTS` as the target PR. If no target is provided, ask for it before doing anything.
## Triage
1. Inspect local state with `git status --short --branch`. If unrelated dirty files exist, do not overwrite or stage them.
2. Fetch current main before making branch decisions:
```sh
git fetch origin main
```
3. Gather PR context:
```sh
gh pr view <pr> --repo remix-run/react-router --json number,title,body,state,isDraft,author,baseRefName,headRefName,headRepository,headRepositoryOwner,maintainerCanModify,mergeStateStatus,reviewDecision,labels,files,commits,statusCheckRollup,url
gh pr checks <pr> --repo remix-run/react-router
gh pr diff <pr> --repo remix-run/react-router --stat
gh pr view <pr> --repo remix-run/react-router --comments
```
4. Identify merge blockers. In particular:
- If a CLA check or comment shows the author has not signed the CLA, use the unsigned-CLA replacement workflow.
- If the PR only needs repo-maintainer additions such as a change file or docs, use the contributor-branch workflow.
- If the blocker is unclear, summarize the evidence and ask the user which path to take.
5. Evaluate test coverage before deciding the finish-line changes:
- Inspect the PR diff, changed files, existing nearby tests, review comments, and failed checks for test expectations.
- If the PR changes runtime behavior, build/plugin behavior, routing semantics, generated types, RSC behavior, docs rendering, or any bug/feature surface that can regress, add or preserve a focused test unless equivalent coverage already exists.
- If tests are already included, verify they exercise the changed behavior and cover the affected React Router mode(s): Declarative, Data, Framework, RSC Data, and/or RSC Framework.
- If tests are not needed because the change is documentation-only, packaging-only, a change file, or otherwise not executable behavior, note that rationale in the final report.
- If a useful test is required but too large or risky for the finish-line scope, stop and ask the user before broadening the PR.
## Unsigned CLA Replacement
Use this path when the PR author's CLA is not signed. Do not merge, cherry-pick, rebase, or push the contributor's commits. Use the PR diff as the behavior/content reference and recreate the final file changes in maintainer-authored commits from current `origin/main`.
1. Save the original PR title, body, labels, changed-file list, and diff for reference.
2. Create a fresh branch from current main:
```sh
git checkout -B brophdawg11/finish-line-pr-<pr-number> origin/main
```
3. Recreate the same resulting changes on the fresh branch. Keep the implementation as close as possible to the original PR unless main has moved and a tiny adaptation is required.
4. Add any missing finish-line work, such as tests, a change file, or docs, if those are also required.
5. Run focused validation that matches the touched area. Prefer the narrowest meaningful test/build command.
6. Commit the recreated changes with a concise imperative subject.
7. Before pushing/opening the replacement PR, read `.agents/skills/create-pr/SKILL.md` and follow its current branch, PR body, and label guidance unless this skill gives a more specific instruction for replacement PRs.
8. Push the maintainer branch and open a replacement PR against `main`.
- Reuse the original title unless it is misleading.
- Use a similar description, but make it clear this is a agent/maintainer-authored replacement.
- Include the old PR number in the description (`#<pr-number>`).
- Default to a ready PR when validation passed and the original PR was otherwise mergeable; use a draft PR if validation is incomplete or the original PR was draft.
- Apply the relevant labels from the original PR plus any package/feature labels required by `.agents/skills/create-pr/SKILL.md`.
9. Comment on the original PR and close it after the replacement PR exists:
```markdown
Thanks for the PR! We can't merge this without the CLA being signed, so we're going to re-implement this work in #<new-pr-number> to keep this moving.
```
Then run:
```sh
gh pr comment <old-pr-number> --repo remix-run/react-router --body-file <comment-file>
gh pr close <old-pr-number> --repo remix-run/react-router
```
## Contributor-Branch Workflow
Use this path when the contributor's CLA is signed and the missing work is small maintainer follow-up, such as a change file or docs.
1. Check out the PR branch:
```sh
gh pr checkout <pr-number> --repo remix-run/react-router
```
2. Confirm the branch and local state:
```sh
git status --short --branch
git branch --show-current
```
3. Make only the missing finish-line changes, including focused tests when the coverage evaluation requires them. Do not refactor the contributor's work unless it is necessary to unblock mergeability and the user agrees.
4. Validate the exact content with the user before pushing:
- For a change file, show the package path, file name, change type, and full markdown contents.
- For docs, show the affected files and the relevant prose/API snippets.
- For tests, show the test file path, the behavior covered, and the mode(s) covered.
- Ask explicitly for approval to commit and push back to the PR branch.
5. After approval, run focused validation when appropriate, commit the maintainer follow-up, and push to the PR branch. If `git push` fails because the contributor branch cannot be modified, stop and report the failure instead of opening a replacement PR unless the user approves that pivot.
## Change Files
Create change files under the affected package:
```text
packages/<package>/.changes/<type>.<unique-meaningful-name>.md
```
Use `patch`, `minor`, `major`, or `unstable` for `<type>`. For bug fixes and narrow behavior fixes, default to `patch`. Keep the content concise:
```markdown
Brief description of the user-facing change
```
If the PR spans multiple packages, prefer the package with the direct user-facing API or runtime behavior. Ask the user when the package or change type is not obvious.
## Documentation
Do not add documentation by default for ordinary bug fixes. Add docs when the PR changes a documented API, introduces new behavior users need to discover, changes examples, or the user/reviewer explicitly requested docs.
Follow repo docs rules:
- Edit source docs or JSDoc, not generated `docs/api/` output.
- Include mode context when adding docs for React Router behavior: Declarative, Data, Framework, RSC Data, or RSC Framework.
- For API docs generated from JSDoc, edit `packages/react-router/lib/` comments and note that `pnpm run docs` may be required.
## Final Report
Report the path taken and the current PR state:
- Original PR number and blocker.
- Whether changes were pushed to the contributor branch or a replacement PR was opened.
- Branch, commit hash, and PR URL when applicable.
- Any old-PR comment/close action taken.
- Validation performed or skipped.
- Test coverage decision: added, already present, or intentionally omitted with rationale.
# Declarative Mode
Declarative Mode is React Router's simplest mode. It uses router components like `<BrowserRouter>` and JSX routes with `<Routes>`/`<Route>`. It does not provide loaders, actions, fetchers, or data-router pending UI.
Use this reference after the main skill identifies a Declarative Mode app.
## Read the Local Docs by Mode
Start with:
```txt
react-router/docs/start/modes.md
react-router/docs/start/declarative/index.md
```
Then use the Declarative docs under:
```txt
react-router/docs/start/declarative/
```
Those files cover installation, routing, navigation, and URL values. For conceptual details, read relevant files in:
```txt
react-router/docs/explanation/
```
Always check the `[MODES: declarative, ...]` marker in a doc before applying it.
## Declarative Router Shape
Typical setup:
```tsx
import { BrowserRouter, Routes, Route } from "react-router";
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
<Route path="dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="settings" element={<Settings />} />
</Route>
</Routes>
</BrowserRouter>
);
}
```
Look for APIs such as:
- `<BrowserRouter>`
- `<HashRouter>`
- `<MemoryRouter>`
- `<Routes>`
- `<Route>`
- `element={<Component />}`
- `useRoutes`
## Routing
Before editing routes, read:
```txt
react-router/docs/start/declarative/routing.md
```
Rules:
- Use `<Routes>` and `<Route>` for route configuration.
- Use nested routes with `<Outlet>` for shared layout.
- Use index routes for default child UI.
- Use route params and splats according to the declarative routing docs.
- Do not add route object loaders/actions to a Declarative router.
## Navigation
Before changing navigation, read:
```txt
react-router/docs/start/declarative/navigating.md
```
Rules:
- Use `<Link>` or `<NavLink>` for user-initiated internal navigation.
- Use `NavLink` when active styling matters.
- Use `useNavigate` for imperative navigation from event handlers or effects.
- Do not use plain `<a href>` for internal navigation unless intentionally forcing a full document navigation.
## URL Values
Before changing params, search params, or location state, read:
```txt
react-router/docs/start/declarative/url-values.md
react-router/docs/explanation/location.md
```
Rules:
- Use `useParams` for dynamic route params.
- Use `useSearchParams` for query string state.
- Use `useLocation` for the current location object and navigation state.
- Validate and parse URL params; they are strings and can be absent.
- Preserve unrelated search params unless intentionally resetting them.
## Mode Boundary
Declarative Mode does not have Data/Framework APIs such as:
- `loader`
- `action`
- `<Form>`
- `useFetcher`
- `useNavigation`
- route module exports
- generated `./+types` route types
If the user asks for route data loading, DB/API-backed data, CRUD, form mutations, validation returned from submissions, revalidation, pending UI, optimistic UI, or fetchers, recommend Data Mode or Framework Mode depending on how much structure they want. Ask before migrating unless they already requested it.
# Framework Mode
Framework Mode is React Router's full-stack mode. It uses the React Router Vite plugin, route config in `app/routes.ts`, route modules, generated route types, and rendering strategies such as SSR, SPA mode, and pre-rendering.
Use this reference after the main skill identifies a Framework Mode app.
## Read the Local Docs by Mode
Start with:
```txt
react-router/docs/start/modes.md
react-router/docs/start/framework/index.md
```
Then use the Framework docs under:
```txt
react-router/docs/start/framework/
```
Those files cover installation, routing, route modules, data loading, actions, navigation, pending UI, rendering, deploying, and testing. For task-specific details, read relevant files in:
```txt
react-router/docs/how-to/
react-router/docs/explanation/
```
Always check the `[MODES: framework, ...]` marker in a doc before applying it.
## Framework Shape
Examples usually assume the default `appDirectory` of `app`. Check `react-router.config.ts` before assuming exact paths.
Look for these files and conventions:
```txt
react-router.config.ts
app/root.tsx
app/routes.ts
app/routes/**/*.tsx
route modules importing from ./+types/...
```
Typical route module:
```tsx
import type { Route } from "./+types/product";
export async function loader({ params }: Route.LoaderArgs) {
return { product: await getProduct(params.productId) };
}
export default function Product({ loaderData }: Route.ComponentProps) {
return <h1>{loaderData.product.name}</h1>;
}
```
## Route Configuration
Framework apps use `app/routes.ts`. Many apps use file-system routing via `flatRoutes()`, but manual route config is also supported.
Before editing routes, read:
```txt
react-router/docs/start/framework/routing.md
```
If the app uses file-route conventions, read:
```txt
react-router/docs/how-to/file-route-conventions.md
```
## Route Modules
Route modules are the main unit of Framework Mode. Before adding or changing route exports, read:
```txt
react-router/docs/start/framework/route-module.md
```
Common exports include:
| Export | Use |
| --------------------------------- | ------------------------------------------------------------------- |
| `default` | Route component rendered for the match |
| `loader` | Server data loading for SSR/pre-rendering/server data requests |
| `clientLoader` | Browser-only data loading or supplementing server loader data |
| `action` | Server mutation called by `<Form>`, `useSubmit`, or fetchers |
| `clientAction` | Browser-only mutation or client-side wrapper around a server action |
| `ErrorBoundary` | UI for errors thrown by this route's loaders/actions/component |
| `HydrateFallback` | Initial fallback while client loader hydration runs |
| `links` / `meta` | Route document links and metadata |
| `handle` | Arbitrary route metadata consumed via `useMatches` |
| `shouldRevalidate` | Overrides default loader revalidation behavior |
| `middleware` / `clientMiddleware` | Server/client request pipeline hooks when enabled |
Use generated `Route.*` types from `./+types/<route>` for route module args and props.
## Layout and Root Route Rules
- `app/root.tsx` is the root route and should contain global document/app shell concerns.
- Put global providers, app-wide nav, app-wide footer, scripts/meta/links, and document structure in `root.tsx` when appropriate.
- Use nested routes/layout routes for section-specific layouts.
- Do not flatten routes that should share UI or data boundaries.
Useful docs:
```txt
react-router/docs/explanation/special-files.md
react-router/docs/start/framework/routing.md
```
## Data and Mutations
Before working on route data:
```txt
react-router/docs/start/framework/data-loading.md
react-router/docs/start/framework/actions.md
```
Framework rules:
- Load route data with `loader` or `clientLoader`.
- Mutate route data with `action` or `clientAction`.
- Prefer route loaders/actions over ad hoc `useEffect` fetching for route data.
- Use `data()`/Responses and redirects according to the docs.
- Let React Router revalidate after actions unless the docs point you to `shouldRevalidate`.
- In SSR/server data routes, keep Node-only/database code in server-only modules and call it from `loader`/`action`, not from browser-rendered component code.
Common patterns:
- Validation failure from an action: return `data({ errors, values }, { status: 400 })`, then render errors from `Route.ComponentProps["actionData"]` or `fetcher.data`.
- Missing record in a loader: throw `data("Not Found", { status: 404 })` and render the route `ErrorBoundary`.
- Search/filter data: parse the route request URL/search params in the loader so the URL is shareable and bookmarkable.
## Forms, Fetchers, and Pending UI
For forms and pending UI, read:
```txt
react-router/docs/start/framework/actions.md
react-router/docs/start/framework/pending-ui.md
react-router/docs/how-to/fetchers.md
react-router/docs/explanation/form-vs-fetcher.md
```
Rules of thumb:
- Search/filter form that updates the URL: `<Form method="get">`.
- Mutation that should change URL/history or redirect after completion: `<Form method="post">`.
- Mutation that should keep the user on the same page: `useFetcher` / `<fetcher.Form>`.
- Optimistic UI: derive from `fetcher.formData` or `navigation.formData`.
## Type Safety
Before changing generated route types or typed URL behavior, read:
```txt
react-router/docs/how-to/route-module-type-safety.md
react-router/docs/explanation/type-safety.md
```
Rules:
- Import types from `./+types/<route>`.
- Use `Route.LoaderArgs`, `Route.ActionArgs`, `Route.ComponentProps`, etc.
- Use type-only imports where appropriate.
- Do not edit generated `.react-router/types` files.
## Metadata
Before changing `meta`, read:
```txt
react-router/docs/how-to/meta.md
react-router/docs/start/framework/route-module.md
```
Important: `meta` receives `loaderData`; do not use deprecated `data` args.
## Rendering Strategy
Framework Mode can be SSR, SPA, pre-rendered, or mixed depending on config and route behavior. Before changing rendering behavior, read:
```txt
react-router/docs/start/framework/rendering.md
react-router/docs/how-to/spa.md
react-router/docs/how-to/pre-rendering.md
react-router/docs/explanation/hydration.md
```
## Middleware, Sessions, and Auth
Before implementing middleware or auth/session flows, read:
```txt
react-router/docs/how-to/middleware.md
react-router/docs/explanation/sessions-and-cookies.md
```
Middleware and context APIs are version/config sensitive. Check the installed React Router version and the app's `react-router.config.ts` before implementing.
## RSC Framework
If this Framework app uses `unstable_reactRouterRSC` or `@vitejs/plugin-rsc`, also read:
```txt
references/rsc.md
react-router/docs/how-to/react-server-components.md
```
---
name: prepare-release-notes
description: Prepare React Router release notes before running the changes/versioning scripts. Use when asked to review, polish, normalize, or prepare pending change files under packages/*/.changes, remove semantic commit prefixes from release bullets, enforce imperative tense, decide whether a manual scripts/changes/whats-changed.md section is warranted, or draft long-form release notes for new features, stable future flags, or unstable flags.
---
# Prepare Release Notes
Polish pending React Router change files and add manual release notes only when the release needs narrative context beyond the generated change lists.
## Workflow
1. Inspect local state:
```sh
git status --short
find packages -path '*/.changes/*.md' -not -name README.md -not -name .gitkeep -print | sort
```
2. Read every pending change file. Do not edit generated changelogs or released notes directly.
3. Normalize each change file:
- Remove `feat:`, `feat(...)`, `fix:`, and `fix(...)` semantic-commit prefixes from prose
- Use present or imperative tense for the first line and top-level release bullets: prefer `Add`, `Fix`, `Remove`, `Support`, `Stabilize`, `Preserve`, `Update`, `Avoid`, `Prevent`, `Throw`, `Warn`, `Expose`
- Nested detail bullets can stay explanatory when they expand on the parent bullet; do not rewrite them solely to force present or imperative tense
- Remove terminal sentence periods from bullet items because release generation appends PR/commit links after the first bullet line
- Remove terminal sentence periods from nested bullet items too, unless the punctuation is part of code, a URL, an abbreviation, a version number, or another token where removing it would be wrong
- If one bullet contains multiple sentences, split it into a shorter parent bullet plus nested bullet items
- Keep the first line concise and user-facing; use nested bullets for details or migration notes
4. Review whether `scripts/changes/whats-changed.md` is needed:
- Read `CHANGELOG.md` examples or `references/whats-changed.md` when uncertain
- Add `scripts/changes/whats-changed.md` only for features, future flag stabilizations, unstable flags, migration guidance, breaking bug fixes, or complex behavior that needs long-form text or examples
- Do not add it for ordinary bug fixes, dependency cleanup, internal refactors, or release bullets that are already clear
- If adding it, write the body only; the release script adds `### What's Changed` when missing
5. Validate:
```sh
pnpm changes:validate
pnpm changes:preview
```
Use `changes:preview` to inspect the generated root release notes and confirm the PR/commit link placement, section ordering, and any manual What's Changed placement. If dependencies are missing or the command is too expensive for the context, state what was skipped.
## Change File Style
Single-line entries should read well with an auto-appended PR link:
```markdown
Fix `href()` to stringify and URL-encode param values like `generatePath()`
```
Use nested bullets for additional sentences:
```markdown
Fix route ranking for dynamic parameters with static extension suffixes
- Identify `/:name.xml` as a dynamic segment instead of a static segment
- Preserve static route priority for paths like `/sitemap.xml`
```
Avoid semantic commit prefixes:
```markdown
Add support for nub as a package manager
```
not:
```markdown
feat: add support for nub as a package manager.
```
## What's Changed Guidance
Use `scripts/changes/whats-changed.md` for release-level narrative, not package-specific bullets. Good candidates include:
- A new user-facing API or feature that benefits from example code
- Stabilization or renaming of unstable APIs/flags, especially when adopters must migrate
- A stable future flag that changes behavior and needs adoption guidance
- A breaking bug fix or adapter/runtime behavior change that may require deployment checks
- A cluster of related changes whose combined effect matters more than the individual bullet list
Keep the tone direct and practical. Prefer headings under the generated `### What's Changed` section:
````markdown
#### Feature Name
Explain what changed, who it affects, and how to adopt it.
```ts
// Optional short example
```
````
Do not duplicate every bullet from Minor/Patch/Unstable Changes. Let generated change files carry ordinary PR-level details.
See `references/whats-changed.md` for examples distilled from the existing changelog.
---
name: fix-bug
description: "Fix a reported bug in React Router from a GitHub issue. Use when the user provides a GitHub issue URL and asks to fix a bug, investigate an issue, or reproduce a problem. Handles the full workflow: fetching the issue, finding the reproduction, writing a failing test, and implementing the fix."
disable-model-invocation: true
---
# Fix React Router Bug
Fix the bug reported in the following GitHub issue: $ARGUMENTS
## Branching
Bug fixes should start from a clean working tree. If there are changes, prompt me to resolve them before continuing.
Bugs should be fixed from the `main` branch in a new branch using the format `{author}/{semantic-branch-name}` (i.e., `brophdawg11/fix-navigation`):
```sh
git branch {author}/{semantic-branch-name} main
git checkout {author}/{semantic-branch-name}
```
## Workflow
### 1. Fetch and Understand the Issue
Use `gh issue view <number> --repo remix-run/react-router` or `WebFetch` to read the full issue.
Extract:
- Bug description and expected vs actual behavior
- React Router version and mode (Declarative / Data / Framework / RSC)
- Any code snippets in the issue
- Links to reproductions (StackBlitz, CodeSandbox, GitHub repo, etc.)
### 2. Validate the Reproduction
**If there's a StackBlitz/CodeSandbox/online sandbox link:**
- Use `WebFetch` to read the sandbox URL and extract the relevant code
- Identify the exact sequence of events that triggers the bug
**If there's a GitHub repository link:**
- Use `WebFetch` to read key files (`package.json`, relevant source files) from the raw GitHub URL
- Identify the route configuration, loaders, actions, or components involved
**If no reproduction link exists:**
- Search the issue comments with `gh issue view <number> --repo remix-run/react-router --comments`
- Look for code snippets in comments
- Ask the user: "No reproduction was provided. Can you share a minimal reproduction or paste the relevant code?"
### 3. Identify the Affected Code
Based on the bug, locate the relevant source files. Consult the key file map:
| Area | Files |
| ---------------------- | ----------------------------------------------------------- |
| Core router logic | `packages/react-router/lib/router/router.ts` |
| React components/hooks | `packages/react-router/lib/components.tsx`, `lib/hooks.tsx` |
| DOM utilities | `packages/react-router/lib/dom/` |
| Vite/Framework plugin | `packages/react-router-dev/vite/plugin.ts` |
| RSC | `packages/react-router/lib/rsc/` |
Use `Grep` and `Glob` to trace the relevant code paths.
### 4. Write a Failing Test
**Unit test** (for router logic, hooks, pure component behavior โ no build needed):
- Location: `packages/react-router/__tests__/`
- Use Jest; run with: `pnpm test packages/react-router/__tests__/<file>`
- Match the style of nearby test files (describe/it blocks, `createStaticHandler`, `createMemoryRouter`, `render`, `screen`, etc.)
**Integration test** (for Vite plugin, SSR, hydration, Framework Mode):
- Location: `integration/`
- Use Playwright with `createFixture()` โ `createAppFixture()` โ `PlaywrightFixture`
- Run with: `pnpm test:integration:run --project chromium integration/<file>`
- Build first if needed: `pnpm test:integration --project chromium`
Write the test to **reproduce the bug exactly** โ it must fail before the fix.
Run it and confirm it fails:
```bash
pnpm test packages/react-router/__tests__/<file> # unit
# or
pnpm test:integration:run --project chromium integration/<file> # integration
```
### 5. Implement the Fix
- Make the minimal change needed to fix the bug
- Do not refactor unrelated code
- Confirm the fix addresses the root cause, not just the symptom
- Consider all five modes: does this fix break anything in Declarative / Data / Framework / RSC?
Run the failing test again โ it must now pass:
```bash
pnpm test packages/react-router/__tests__/<file>
```
Run the broader test suite to check for regressions:
```bash
pnpm test packages/react-router/
```
If the fix touches Framework/Vite code, run integration tests too:
```bash
pnpm test:integration:run --project chromium
```
Confirm linting and typechecking pass:
```bash
pnpm lint
pnpm typecheck
```
### 6. Create a Change file
Create a change file at `packages/<package>/.changes/<type>.<unique-meaningful-name>.md`. `<type>` should be either `patch`, `minor`, `major` or `unstable` to indicate the type of API change being made.
Format:
```markdown
fix: <brief description of what was fixed>
```
### 7. Report Results
Summarize:
- What the bug was and why it happened
- What code was changed and why
- That the test now passes
- Any edge cases or related issues noticed
Ask me to review the changes and iterate based on any feedback.
### 8. Open PR
Once I approve the fix, commit the changes and open a PR to `main`. Include a `Closes #NNNN` in the description to link the PR to the original issue. Also link the issue in the `Development` sidebar
Discover similar high-velocity repositories, agent skills, and OpenAPI specifications across the ecosystem.
Topic hubs, agent specifications, and quick tools