# Pathrule Pattern: React Router 7 (1.0.0)
# ::pathrule:package:react-router

### [RULE] Load and mutate through loaders and actions  (path: /app/routes)
<!-- scope: folder | priority: high | advisory -->

In framework mode, route data and mutations live on the route module, not in component effects.

- Read data with `loader` (server) or `clientLoader` (browser); never fetch route data in `useEffect`.
- Mutate with `action` / `clientAction` submitted via `<Form method="post">` or `useSubmit`, not `fetch` in click handlers. `<Form>` works before hydration, so submissions degrade gracefully without JS.
- After an action resolves, every matched route loader revalidates automatically. Do not manually refetch. If you see stale data, check that a `shouldRevalidate` export is not returning `false` and blocking the re-run.
- Use `useFetcher` for non-navigation mutations (likes, inline edits, autosave): it submits to an action and revalidates affected loaders without changing the URL.
- For control flow, `throw redirect("/path")` from a loader/action instead of navigating imperatively in an effect.
- Put server-only secrets and DB calls inside `loader`/`action`; that code is stripped from the client bundle. `clientLoader`/`clientAction` ship to the browser, so never put secrets there.

---

### [RULE] Use generated Route types, not hand-written ones  (path: /app/routes)
<!-- scope: folder | priority: high | advisory -->

React Router codegen emits a typed `Route` namespace per route file; use it instead of casting.

- Import with `import type { Route } from "./+types/<route-file>"` and type handlers as `Route.LoaderArgs`, `Route.ClientLoaderArgs`, `Route.ActionArgs`, `Route.ComponentProps`, `Route.ErrorBoundaryProps`, `Route.HydrateFallbackProps`.
- The `+types/<name>` segment must mirror the route file name exactly, including `$` params and `.` separators (for example `./+types/posts.$id`). A mismatch is the most common "types are missing/wrong" cause.
- Read `params`, `loaderData`, and `actionData` from the typed props rather than `useParams()` casts; `params` is typed from the route pattern and `loaderData` is inferred from the loader return.
- Generated declarations live in `.react-router/types/`; they require `rootDirs` in `tsconfig.json` to resolve as if adjacent to the route. Keep `react-router dev` (or `react-router typegen --watch`) running so they stay current. Never hand-edit generated files and do not commit them.

---

### [RULE] Keep the route config and codegen in sync  (path: /app)
<!-- scope: folder | priority: medium | advisory -->

Framework-mode routing is driven by `app/routes.ts`, and the codegen depends on it being correct.

- Define routes with the config helpers: `import { type RouteConfig, route, index, layout, prefix } from "@react-router/dev/routes"`.
- Once `app/routes.ts` exists, the file-system convention is OFF by default. You must declare every route, or routes silently 404. To keep convention-based discovery, spread it back in explicitly: `import { flatRoutes } from "@react-router/fs-routes"` then `export default [route("/", "./home.tsx"), ...(await flatRoutes())] satisfies RouteConfig`.
- Use `layout(file, children)` to nest UI without adding a URL segment, and `prefix(path, routes)` to add a path prefix without a new route module. Do not fake these with empty path segments.
- Every route module referenced in `routes.ts` must exist and export a `default` component (or be a pure layout/resource route); a dangling reference breaks typegen for the whole app.

---

### [MEMORY] Framework mode data flow and single-fetch  (path: /app)

Framework mode (the Remix-merged full-stack setup) drives the data lifecycle from route modules. We are on `react-router` 7.17.x; `react-router-dom` is folded into `react-router`, so import everything from `react-router`.

- One navigation triggers a single HTTP request (single-fetch) that resolves every matched route's loader together. Avoid waterfalling per-component fetches; co-locate data needs on the route loaders.
- On initial load/SSR the server `loader` runs; on client navigations the server loader is called via an automatic browser fetch. `clientLoader` runs only on the client unless you opt it into hydration.
- To run a `clientLoader` during hydration, export `clientLoader.hydrate = true as const` and provide a `HydrateFallback` component to render while it runs.
- Stream slow data: return a promise from a loader and render it with `<Await>` + `<Suspense>` (or `useAsyncValue`) so the shell paints immediately. Loader serialization also handles Dates, Maps, Sets, and promises, not just primitives.
- Single-fetch merges loader responses; when multiple matched loaders set headers, the deepest matching route wins. Set response headers from the leaf loader/action that owns them.

See /app/routes for the load/mutate and generated-types rules, and the middleware memory at /app for the request-pipeline layer.

---

### [MEMORY] Middleware and request context (v8_middleware)  (path: /app)

React Router 7.17 ships middleware behind a future flag; it is the canonical place for auth, logging, and seeding per-request context. It is slated to become default in v8, so the API is stable enough to adopt now.

- Enable it in `react-router.config.ts`: `export default { future: { v8_middleware: true } } satisfies Config`. In data mode (`createBrowserRouter`) you also augment the `Future` interface via `declare module "react-router"`.
- A route exports `middleware: Route.MiddlewareFunction[]` (server) and/or `clientMiddleware: Route.ClientMiddlewareFunction[]`. Each entry receives `({ request, context }, next)` and runs before that route's loader/action.
- Contract: call `await next()` to continue the chain; on the SERVER you must return the response (`return await next()`), and post-`next()` code runs after handlers (for timing, header rewriting). On the client `next()` is optional. `next()` may be called at most once and never throws; thrown errors route to the nearest `ErrorBoundary`.
- Share data through typed context, not module globals: `const userCtx = createContext<User | null>(null)`, then `context.set(userCtx, user)` in middleware and `context.get(userCtx)` in downstream middleware/loaders. With a custom server, return a `new RouterContextProvider()` from `getLoadContext`.
- Auth pattern: in middleware, load the session and `throw redirect("/login")` when missing, otherwise `context.set(userCtx, user)` so every loader on the route reads an authenticated user without re-checking.

See /app for the single-fetch data-flow memory and /app/routes for the loader/action rules that consume this context.

---

### [SKILL] react-router-review  (path: /)

---
name: react-router-review
description: Review a React Router 7 framework-mode change before merging. Use when adding or changing route modules, loaders, actions, middleware, or app/routes.ts.
---

# React Router 7 review

## Data and mutations
- [ ] Route data is read via `loader` / `clientLoader`, not `useEffect` fetches
- [ ] Mutations go through `action` / `clientAction` submitted with `<Form>` or `useFetcher`, not raw `fetch` in handlers
- [ ] No manual refetch after an action; rely on automatic revalidation (check any `shouldRevalidate` does not block needed loaders)
- [ ] `useFetcher` is used for non-navigation mutations (likes, inline edits) instead of changing the URL
- [ ] Slow data is deferred with a returned promise + `<Await>` / `<Suspense>` instead of blocking the whole route
- [ ] Redirects use `throw redirect()` from loaders/actions, not imperative navigation in effects

## Type safety
- [ ] Args and props use generated `Route.LoaderArgs` / `Route.ActionArgs` / `Route.ComponentProps` from `./+types/...`
- [ ] The `+types/<name>` import path mirrors the route file name exactly (including `$` params and `.` separators)
- [ ] `params`, `loaderData`, `actionData` come from typed props, not `useParams()` casts
- [ ] No generated files (`.react-router/types/`) were hand-edited or committed

## Routing config
- [ ] New route is registered in `app/routes.ts` via `route` / `index` / `layout` / `prefix` (or covered by a spread `flatRoutes()`)
- [ ] Every referenced route module exists and exports a `default` (or is a deliberate layout/resource route)

## Middleware and security
- [ ] Server-only code (secrets, DB) stays inside `loader` / `action` / `middleware`, never in `clientLoader` / `clientAction`
- [ ] Middleware calls `await next()` and returns its response on the server; shared state goes through `createContext` + `context.set/get`, not module globals
- [ ] Auth/redirect checks live in `middleware` (or a loader) rather than being duplicated per component

## Resilience
- [ ] `<Link>` uses an appropriate `prefetch` mode (`intent` / `viewport` / `render`) for hot navigations
- [ ] Route exports an `ErrorBoundary` (and `meta` where needed) for failure and SEO paths
