# react-router — corrections from the Stale Priors Index

<!--
     Stale Priors Index · Unattended Works · https://stalepriors.com

     GENERATED FILE — DO NOT EDIT BY HAND. Your edit will be destroyed by the next build.
     Rebuild:  node tools/build-rules.mjs
     Source:   data/react-router/facts.json
-->

For projects on **react-router@^7.12 || ^8**. 6 corrections, 6 of them dated to the release that changed the behaviour; every entry links the primary source it was verified against. Latest react-router: 8.3.1, verified 2026-09-09. Do **not** use this if you are pinned to react-router@6.x, and the separate `react-router-dom` package, which has no v8 release (LF5).

**This is the statements only** — no reproduced model failures, no code, no evidence. The full pack has all three: https://stalepriors.com/corrections/react-router.md

**What was measured, and what this file is not.** The one benchmark run so far measured the **zod** correction pack — not react-router's, and not this shorter file. There is no measurement of what either does for react-router. Every cell, including the metric that found no difference: https://stalepriors.com/benchmark

The corrections below are 6,074 characters. Read that as a cost: a rules file is added to requests, so it is a decision to make rather than a default to accept.

## Breaks the build, or throws at runtime

### `react-router-dom`

**Removed in react-router 8.0.0** (2026-06-17).

The `react-router-dom` package is not published on the v8 line — 8.0.0 removed it. Its npm `latest` tag still points at **7.18.3**, while `react-router`'s `latest` is on the 8.x line, so `npm install react-router-dom` in a v8 project installs a v7 router beside your v8 one and leaves two major lines of React Router in the same tree. On v8 there is no DOM package to install: import `RouterProvider` and `HydratedRouter` from `react-router/dom`, and everything else from `react-router`. In v7 this was already the real layout — `react-router-dom` was kept only as a re-export so v6 imports would keep resolving.

*The stale belief:* That a browser React Router app installs and imports from `react-router-dom`, which is where `BrowserRouter`, `RouterProvider`, `Link` and the hooks live.

*Verified against:* [react-router CHANGELOG.md — 8.0.0 major changes](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)

### `react-router@8 engine and peer requirements`

**New requirement in react-router 8.0.0** (2026-06-17).

react-router 8 raised both floors: `engines.node` is `>=22.22.0` and the `react` / `react-dom` peer range is `>=19.2.7`. On the 7.x line they are `>=20.0.0` and `>=18`. A project on Node 20 or React 18 that installs `react-router@latest` gets a peer-dependency failure from npm (or a silently unmet peer under `--legacy-peer-deps`), not a runtime deprecation warning. Pin `react-router@^7` if either floor is a problem; the 7.x line is still published and patched.

*The stale belief:* That react-router runs on Node 20 and React 18, which was true for the whole 7.x line.

*Verified against:* [react-router CHANGELOG.md — 8.0.0 major changes](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)

## Runs, but is silently wrong

### `pattern (loader / action argument)`

**Renamed in react-router 7.15.0** (2026-05-05).

The instrumentation APIs lost their `unstable_` prefix in react-router 7.15.0. The route pattern handed to a `loader`/`action` is `args.pattern`, not `args.unstable_pattern`, and the router option is `instrumentations`, not `unstable_instrumentations`; the `unstable_ServerInstrumentation`, `unstable_ClientInstrumentation`, `unstable_InstrumentRequestHandlerFunction`, `unstable_InstrumentRouterFunction`, `unstable_InstrumentRouteFunction` and `unstable_InstrumentationHandlerResult` types dropped the prefix too. The old names are **gone, not aliased**: reading `args.unstable_pattern` on 7.15.0 and above yields `undefined` in JavaScript rather than a type error, so a telemetry span or log line built from it is labelled `undefined`.

*The stale belief:* That the route pattern inside a loader is `args.unstable_pattern` and the router option is `unstable_instrumentations`, because the instrumentation API is still unstable.

*Verified against:* [react-router CHANGELOG.md — 7.15.0](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)

### `url (loader / action / middleware argument)`

**Added in react-router 7.15.0** (2026-05-05).

`loader`, `action` and `middleware` receive a `url` argument since react-router 7.15.0, where it was stabilised from `unstable_url`. It is the **normalised** URL: React Router's own transport details — the `.data` suffix on client data requests and the `index` / `_routes` search params — are already stripped from it. Read `args.url` rather than `new URL(args.request.url)` when you want the URL the user is on. This matters most on the v8 line, where the `future.v8_passThroughRequests` flag was removed and the raw incoming `request` is now always what your loader gets, so `request.url` can carry those suffixes.

*The stale belief:* That the only URL available inside a loader or action is `request.url`, and that parsing it with `new URL(...)` yields the route's own pathname.

*Verified against:* [react-router CHANGELOG.md — 7.15.0](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)

### `generatePath`

**Behaviour changed in react-router 7.12.0** (2026-01-07).

`generatePath` substitutes a dynamic segment that is followed by a literal suffix since react-router 7.12.0. `generatePath("/books/:id.json", { id: "7" })` returns `"/books/7.json"`. On 7.11.0 and below the same call returns the pattern **unchanged** — `"/books/:id.json"` — with no error, no warning and no missing-parameter message, so the URL is silently wrong and a fetch against it 404s at runtime rather than failing at the call site. On 7.12.0 and above, call `generatePath` directly and delete any hand-rolled `String.replace` substitution written to work around this.

*The stale belief:* That `generatePath` only substitutes a parameter that occupies a whole path segment, so a pattern like `/books/:id.json` or `/files/:name.csv` has to be built by string replacement or a template literal.

*Verified against:* [react-router CHANGELOG.md — 7.12.0](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)

## Wrong facts about the library

### `node_modules/react-router/docs`

**Added in react-router 7.17.0** (2026-06-04).

Since react-router 7.17.0 the installed package carries a subset of the official documentation as Markdown at `node_modules/react-router/docs`, so a coding agent working in a project can read that project's own version of the docs off disk instead of fetching a doc site that may describe a different release. The shipped subset is `index.md` plus `start/`, `how-to/`, `explanation/` and `upgrading/`; the auto-generated API reference (`api/`), `community/` and the tutorials are excluded.

*The stale belief:* That the `react-router` npm package ships only `dist/`, a README and a changelog, and that documentation has to come from the web.

*Verified against:* [react-router CHANGELOG.md — 7.17.0](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
