From 69debd1a33aa87924740ec199c4a598d4d1ebcae Mon Sep 17 00:00:00 2001 From: dfedoryshchev <64079946+dfedoryshchev@users.noreply.github.com> Date: Tue, 14 Jul 2026 15:31:46 +0100 Subject: [PATCH 1/4] fix: apply NavLink pending state when the to prop has a trailing slash (#15300) --- .../patch.navlink-pending-trailing-slash.md | 1 + .../__tests__/dom/nav-link-active-test.tsx | 39 +++++++++++++++++++ packages/react-router/lib/dom/lib.tsx | 2 +- 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 packages/react-router/.changes/patch.navlink-pending-trailing-slash.md diff --git a/packages/react-router/.changes/patch.navlink-pending-trailing-slash.md b/packages/react-router/.changes/patch.navlink-pending-trailing-slash.md new file mode 100644 index 0000000000..085ad0f54f --- /dev/null +++ b/packages/react-router/.changes/patch.navlink-pending-trailing-slash.md @@ -0,0 +1 @@ +Fix `NavLink` not applying its `pending` state when `to` has a trailing slash diff --git a/packages/react-router/__tests__/dom/nav-link-active-test.tsx b/packages/react-router/__tests__/dom/nav-link-active-test.tsx index 6213df570b..f5de0a4a71 100644 --- a/packages/react-router/__tests__/dom/nav-link-active-test.tsx +++ b/packages/react-router/__tests__/dom/nav-link-active-test.tsx @@ -696,6 +696,45 @@ describe("NavLink using a data router", () => { expect(screen.getByText("Link to Bar").className).toBe("active"); }); + it("applies the 'pending' className to a NavLink whose 'to' has a trailing slash", async () => { + let dfd = createDeferred(); + let router = createBrowserRouter( + createRoutesFromElements( + }> + }> + dfd.promise} + element={

Child page

} + /> +
+
, + ), + { + window: getWindow("/"), + }, + ); + render(); + + function Layout() { + return ( + <> + Home + Child + + + ); + } + + expect(screen.getByText("Home").className).toBe(""); + + fireEvent.click(screen.getByText("Child")); + expect(screen.getByText("Home").className).toBe("pending"); + + dfd.resolve(null); + await waitFor(() => screen.getByText("Child page")); + }); + it("applies its className correctly when provided as a function", async () => { let dfd = createDeferred(); let router = createBrowserRouter( diff --git a/packages/react-router/lib/dom/lib.tsx b/packages/react-router/lib/dom/lib.tsx index 6e01cb1584..47e66196e6 100644 --- a/packages/react-router/lib/dom/lib.tsx +++ b/packages/react-router/lib/dom/lib.tsx @@ -1690,7 +1690,7 @@ export const NavLink = React.forwardRef( (nextLocationPathname === toPathname || (!end && nextLocationPathname.startsWith(toPathname) && - nextLocationPathname.charAt(toPathname.length) === "/")); + nextLocationPathname.charAt(endSlashPosition) === "/")); let renderProps = { isActive, From baa9ba617b9df1114359439013462cc3486d5b72 Mon Sep 17 00:00:00 2001 From: Nowell Strite Date: Tue, 14 Jul 2026 11:18:22 -0400 Subject: [PATCH 2/4] fix: encode path params per RFC 3986 path-segment rules in href/generatePath (#15310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: encode path params per RFC 3986 path-segment rules in href/generatePath Follow-up to #15277, which fixed `href()` to URL-encode param values to match `generatePath()`. Both now encode with `encodeURIComponent`, which implements *query-string* escaping — a stricter rule than URL paths require. This over-escapes characters that RFC 3986 explicitly allows literally in a path segment: pchar = unreserved / pct-encoded / sub-delims / ":" / "@" sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 `$ & + , ; = : @` only act as delimiters in a query string; in a path segment they carry no special meaning, and browsers keep them literal in `location.pathname`. Encoding them needlessly rewrites URLs: href("/releases/:v", { v: "1.0.0+1" }) // before: /releases/1.0.0%2B1 // after: /releases/1.0.0+1 which breaks apps that compare generated URLs against `window.location.pathname` (the browser reports the literal `+`), churns canonical/shareable URLs, and makes `href()` output disagree with what users see in the address bar. Changes: - Add an internal `encodePathParam()` that escapes structural/unsafe characters (`/ ? # %`, whitespace, non-ASCII) exactly as before, but restores the RFC 3986 pchar set that `encodeURIComponent` over-escapes - Use it for named params in `generatePath()` and for named params and splat segments in `href()` - Document path-segment vs query-string encoding semantics (with the RFC reference) in the `href()` and `generatePath()` JSDoc - Update and extend unit tests; add a change file Behavior is unchanged for every character outside `$ & + , ; = : @`, and `generatePath()`'s splat values remain un-encoded, as before. Co-Authored-By: Claude Fable 5 * Update docs --------- Co-authored-by: Claude Fable 5 Co-authored-by: Matt Brophy --- .../patch.encode-path-params-pchar.md | 4 ++ .../__tests__/generatePath-test.tsx | 22 +++++++ packages/react-router/__tests__/href-test.ts | 27 +++++++- packages/react-router/lib/href.ts | 37 ++++++++--- packages/react-router/lib/router/utils.ts | 61 ++++++++++++++++++- 5 files changed, 138 insertions(+), 13 deletions(-) create mode 100644 packages/react-router/.changes/patch.encode-path-params-pchar.md diff --git a/packages/react-router/.changes/patch.encode-path-params-pchar.md b/packages/react-router/.changes/patch.encode-path-params-pchar.md new file mode 100644 index 0000000000..811f55cafe --- /dev/null +++ b/packages/react-router/.changes/patch.encode-path-params-pchar.md @@ -0,0 +1,4 @@ +Encode path params in `href`/`generatePath` per RFC 3986 path-segment rules instead of `encodeURIComponent` + +- Characters that are valid literally in a path segment (`$ & + , ; = : @` — RFC 3986 `pchar`) are no longer percent-encoded, so values like a semver build `1.0.0+1` interpolate unchanged instead of becoming `1.0.0%2B1` +- Structural/unsafe characters (`/ ? # %`, whitespace, non-ASCII) are still escaped exactly as before diff --git a/packages/react-router/__tests__/generatePath-test.tsx b/packages/react-router/__tests__/generatePath-test.tsx index c0bad0d374..de9fa4ee74 100644 --- a/packages/react-router/__tests__/generatePath-test.tsx +++ b/packages/react-router/__tests__/generatePath-test.tsx @@ -141,6 +141,28 @@ describe("generatePath", () => { }); }); + describe("param encoding", () => { + it("encodes characters that would change the URL structure", () => { + expect(generatePath("/courses/:id", { id: "a b?c#d%e" })).toBe( + "/courses/a%20b%3Fc%23d%25e", + ); + expect(generatePath("/courses/:id", { id: "café…" })).toBe( + "/courses/caf%C3%A9%E2%80%A6", + ); + }); + + it("preserves characters RFC 3986 allows literally in a path segment", () => { + // pchar sub-delims plus ":" and "@" — see RFC 3986 §3.3 + expect(generatePath("/courses/:id", { id: "$&+,;=:@" })).toBe( + "/courses/$&+,;=:@", + ); + // e.g. a semver build suffix survives untouched + expect(generatePath("/releases/:version", { version: "1.0.0+1" })).toBe( + "/releases/1.0.0+1", + ); + }); + }); + it("throws only on missing named parameters, but not missing splat params", () => { expect(() => generatePath(":foo")).toThrow(); expect(() => generatePath("/:foo")).toThrow(); diff --git a/packages/react-router/__tests__/href-test.ts b/packages/react-router/__tests__/href-test.ts index 50d631ec93..4c06e1003f 100644 --- a/packages/react-router/__tests__/href-test.ts +++ b/packages/react-router/__tests__/href-test.ts @@ -52,8 +52,21 @@ describe("href", () => { expect(href("/products/:id", { id: "abc#frag" })).toBe( "/products/abc%23frag", ); + // "?" is escaped (it would start the query string), "=" is not (it is a + // valid pchar in a path segment, unlike in a query string) expect(href("/products/:id", { id: "abc?x=1" })).toBe( - "/products/abc%3Fx%3D1", + "/products/abc%3Fx=1", + ); + }); + + it("preserves characters RFC 3986 allows literally in a path segment", () => { + // pchar sub-delims plus ":" and "@" — see RFC 3986 §3.3 + expect(href("/products/:id", { id: "$&+,;=:@" })).toBe( + "/products/$&+,;=:@", + ); + // e.g. a semver build suffix survives untouched + expect(href("/releases/:version", { version: "1.0.0+1" })).toBe( + "/releases/1.0.0+1", ); }); @@ -61,11 +74,21 @@ describe("href", () => { expect(href("/:param/*", { param: "a?b/c#d", "*": "e?f/g#h" })).toBe( "/a%3Fb%2Fc%23d/e%3Ff/g%23h", ); + expect(href("/releases/*", { "*": "v1/1.0.0+1" })).toBe( + "/releases/v1/1.0.0+1", + ); }); it("round-trips through matchPath for param values with special characters", () => { let pattern = "/products/:id"; - for (let id of ["shoes/2026-summer", "abc#frag", "abc?x=1", "a b"]) { + for (let id of [ + "shoes/2026-summer", + "abc#frag", + "abc?x=1", + "a b", + "$&+,;=:@", + "1.0.0+1", + ]) { let result = href(pattern, { id }); // before the fix, href()'s own output didn't match its own pattern let match = matchPath(pattern, result); diff --git a/packages/react-router/lib/href.ts b/packages/react-router/lib/href.ts index ee82c6d743..d295e82ee1 100644 --- a/packages/react-router/lib/href.ts +++ b/packages/react-router/lib/href.ts @@ -1,3 +1,4 @@ +import { encodePathParam } from "./router/utils"; import type { Pages } from "./types/register"; import type { Equal } from "./types/utils"; @@ -17,14 +18,30 @@ function stringify(p: any) { } /** - Returns a resolved URL path for the specified route. - - ```tsx - const h = href("/:lang?/about", { lang: "en" }) - // -> `/en/about` - - - ``` + * Returns a resolved URL path for the specified route. + * + * Param values are percent-encoded for use in a path segment: characters that + * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) + * are escaped, while characters that RFC 3986 allows literally in a path + * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string + * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are + * delimiters and must be escaped. Splat (`*`) values are encoded per segment, + * preserving `/` separators. + * + * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) + * + * @example + * const h = href("/:lang?/about", { lang: "en" }) + * // -> `/en/about` + * + * + * + * @public + * @category Utils + * @mode framework + * @param path The route path to resolve + * @param args The route params to use when resolving the path + * @returns The resolved URL path */ export function href( path: Path, @@ -42,7 +59,7 @@ export function href( `Path '${path}' requires param '${param}' but it was not provided`, ); } - return value == null ? "" : "/" + encodeURIComponent(stringify(value)); + return value == null ? "" : "/" + encodePathParam(stringify(value)); }, ); @@ -53,7 +70,7 @@ export function href( const value = params?.["*"]; if (value !== undefined) { result += - "/" + stringify(value).split("/").map(encodeURIComponent).join("/"); + "/" + stringify(value).split("/").map(encodePathParam).join("/"); } } diff --git a/packages/react-router/lib/router/utils.ts b/packages/react-router/lib/router/utils.ts index 356c429caa..28f501bbe8 100644 --- a/packages/react-router/lib/router/utils.ts +++ b/packages/react-router/lib/router/utils.ts @@ -1480,13 +1480,72 @@ function matchRouteBranch< return matches; } +/** + * Characters that `encodeURIComponent` escapes but that are valid literally in + * a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`: + * + * ``` + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" + * ``` + * + * `encodeURIComponent` targets query-string values, where `$ & + , ; = : @` + * are delimiters and must be escaped — but in a path segment they carry no + * special meaning, and browsers keep them literal in `location.pathname`. + * (`! ' ( ) *` and the unreserved set are already left alone by + * `encodeURIComponent`, so they need no restoring.) + */ +const PATH_PARAM_OVERESCAPED: Record = { + "%24": "$", + "%26": "&", + "%2B": "+", + "%2C": ",", + "%3A": ":", + "%3B": ";", + "%3D": "=", + "%40": "@", +}; + +/** + * Encodes a param value for interpolation into a single URL path segment. + * + * Escapes characters that would break the path (`/ ? # %`, whitespace, + * non-ASCII, …) while leaving characters that RFC 3986 permits literally in a + * path segment untouched. Escaping those would needlessly rewrite URLs — e.g. + * a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers + * display and match the `+` literally in `location.pathname`. + * + * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)) + * + * @param value The param value to encode. + * @returns The encoded value, safe for use as a single path segment. + */ +export function encodePathParam(value: string): string { + return encodeURIComponent(value).replace( + /%(?:24|26|2B|2C|3A|3B|3D|40)/g, + (match) => PATH_PARAM_OVERESCAPED[match], + ); +} + /** * Returns a path with params interpolated. * + * Param values are percent-encoded for use in a path segment: characters that + * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) + * are escaped, while characters that RFC 3986 allows literally in a path + * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string + * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are + * delimiters and must be escaped. Splat (`*`) values are encoded per segment, + * preserving `/` separators. + * + * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) + * * @example * import { generatePath } from "react-router"; * * generatePath("/users/:id", { id: "123" }); // "/users/123" + * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b" + * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1" * * @public * @category Utils @@ -1532,7 +1591,7 @@ export function generatePath( const [, key, optional, suffix] = keyMatch; let param = params[key as keyof typeof params]; invariant(optional === "?" || param != null, `Missing ":${key}" param`); - return encodeURIComponent(stringify(param)) + suffix; + return encodePathParam(stringify(param)) + suffix; } // Remove any optional markers from optional static segments From f6b681c24542b5066e2b133c2ce493b01387e360 Mon Sep 17 00:00:00 2001 From: Remix Run Bot Date: Tue, 14 Jul 2026 15:19:15 +0000 Subject: [PATCH 3/4] chore: generate markdown docs from jsdocs --- docs/api/utils/generatePath.md | 12 +++++++++ docs/api/utils/href.md | 48 +++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/docs/api/utils/generatePath.md b/docs/api/utils/generatePath.md index 70e46c4597..0bc46bab31 100644 --- a/docs/api/utils/generatePath.md +++ b/docs/api/utils/generatePath.md @@ -24,10 +24,22 @@ https://github.com/remix-run/react-router/blob/main/packages/react-router/lib/ro Returns a path with params interpolated. +Param values are percent-encoded for use in a path segment: characters that +would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) +are escaped, while characters that RFC 3986 allows literally in a path +segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string +encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are +delimiters and must be escaped. Splat (`*`) values are encoded per segment, +preserving `/` separators. + +See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) + ```tsx import { generatePath } from "react-router"; generatePath("/users/:id", { id: "123" }); // "/users/123" +generatePath("/files/:name", { name: "a b" }); // "/files/a%20b" +generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1" ``` ## Signature diff --git a/docs/api/utils/href.md b/docs/api/utils/href.md index 031d0abbf0..2d92485dbd 100644 --- a/docs/api/utils/href.md +++ b/docs/api/utils/href.md @@ -4,17 +4,63 @@ title: href # href + + [MODES: framework] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.href.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.href.html) Returns a resolved URL path for the specified route. +Param values are percent-encoded for use in a path segment: characters that +would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) +are escaped, while characters that RFC 3986 allows literally in a path +segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string +encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are +delimiters and must be escaped. Splat (`*`) values are encoded per segment, +preserving `/` separators. + +See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) + ```tsx const h = href("/:lang?/about", { lang: "en" }) // -> `/en/about` ``` + +## Signature + +```tsx +function href( + path: Path, + ...args: Args[Path] +): string +``` + +## Params + +### path + +The route path to resolve + +### args + +The route params to use when resolving the path + +## Returns + +The resolved URL path + From f75c89fd9cf76177c22d008265d78ee9184c483e Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Tue, 14 Jul 2026 13:23:54 -0400 Subject: [PATCH 4/4] Update docs links to v8 API reference (#15316) * Update docs links to v8 API reference * Generate utility docs from JSDoc * Remove hidden docs generation --- docs/api/data-routers/createStaticRouter.md | 4 +- .../react-router.config.ts.md | 2 +- docs/api/framework-conventions/routes.ts.md | 12 ++--- docs/api/hooks/useFetcher.md | 2 +- docs/api/hooks/useFormAction.md | 4 +- docs/api/hooks/useHref.md | 4 +- docs/api/hooks/useLinkClickHandler.md | 4 +- docs/api/hooks/useResolvedPath.md | 4 +- docs/api/hooks/useViewTransitionState.md | 4 +- docs/api/other-api/adapter.md | 8 ++-- docs/api/utils/IsCookieFunction.md | 35 +++++++++++++- docs/api/utils/IsSessionFunction.md | 35 +++++++++++++- docs/api/utils/createCookie.md | 29 ++++++++++- docs/api/utils/createCookieSessionStorage.md | 34 ++++++++++++- docs/api/utils/createMemorySessionStorage.md | 40 ++++++++++++++-- docs/api/utils/createPath.md | 29 +++++++++-- docs/api/utils/createRequestHandler.md | 12 ----- docs/api/utils/createRoutesStub.md | 36 +++++++++++--- docs/api/utils/createSearchParams.md | 42 ++++++++++------ docs/api/utils/createSession.md | 17 ------- docs/api/utils/createSessionStorage.md | 17 ------- docs/api/utils/generatePath.md | 4 +- docs/api/utils/isCookie.md | 28 ++++++++++- docs/api/utils/isSession.md | 28 ++++++++++- docs/api/utils/parsePath.md | 23 +++++++-- docs/explanation/sessions-and-cookies.md | 24 +++++----- docs/explanation/state-management.md | 4 +- docs/how-to/presets.md | 4 +- docs/how-to/server-bundles.md | 4 +- docs/start/data/route-object.md | 4 +- docs/start/data/routing.md | 2 +- docs/start/framework/pending-ui.md | 2 +- docs/start/framework/route-module.md | 16 +++---- docs/start/framework/routing.md | 2 +- docs/tutorials/address-book.md | 20 ++++---- docs/tutorials/quickstart.md | 2 +- packages/react-router/lib/dom/dom.ts | 48 +++++++++---------- .../lib/dom/ssr/routes-test-stub.tsx | 13 +++++ packages/react-router/lib/router/history.ts | 6 +++ .../lib/server-runtime/cookies.ts | 30 +++++++++++- .../react-router/lib/server-runtime/server.ts | 12 +++++ .../lib/server-runtime/sessions.ts | 33 ++++++++++++- .../server-runtime/sessions/cookieStorage.ts | 8 ++++ .../server-runtime/sessions/memoryStorage.ts | 7 +++ scripts/docs.ts | 23 ++++++++- 45 files changed, 534 insertions(+), 187 deletions(-) delete mode 100644 docs/api/utils/createRequestHandler.md delete mode 100644 docs/api/utils/createSession.md delete mode 100644 docs/api/utils/createSessionStorage.md diff --git a/docs/api/data-routers/createStaticRouter.md b/docs/api/data-routers/createStaticRouter.md index c7355f3921..ff89645598 100644 --- a/docs/api/data-routers/createStaticRouter.md +++ b/docs/api/data-routers/createStaticRouter.md @@ -50,8 +50,8 @@ function createStaticRouter( opts: { branches?: RouteBranch[]; future?: Partial; - } = , -): DataRouter {} + } = {}, +): DataRouter ``` ## Params diff --git a/docs/api/framework-conventions/react-router.config.ts.md b/docs/api/framework-conventions/react-router.config.ts.md index 58b601ab63..4021b2216c 100644 --- a/docs/api/framework-conventions/react-router.config.ts.md +++ b/docs/api/framework-conventions/react-router.config.ts.md @@ -13,7 +13,7 @@ order: 3 This file is optional -[Reference Documentation ↗](https://api.reactrouter.com/v7/types/_react-router_dev.config.Config.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/types/_react-router_dev.config.Config.html) React Router framework configuration file that lets you customize aspects of your React Router application like server-side rendering, directory locations, and build settings. diff --git a/docs/api/framework-conventions/routes.ts.md b/docs/api/framework-conventions/routes.ts.md index 94ba02eef9..b452ddf3b3 100644 --- a/docs/api/framework-conventions/routes.ts.md +++ b/docs/api/framework-conventions/routes.ts.md @@ -13,7 +13,7 @@ order: 2 This file is required -[Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/_react-router_dev.routes.RouteConfigEntry.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/interfaces/_react-router_dev.routes.RouteConfigEntry.html) Configuration file that maps URL patterns to route modules in your application. @@ -59,9 +59,9 @@ export default flatRoutes() satisfies RouteConfig; ### Route Helpers [routing]: ../../start/framework/routing -[route]: https://api.reactrouter.com/v7/functions/_react-router_dev.routes.route.html -[index]: https://api.reactrouter.com/v7/functions/_react-router_dev.routes.index.html -[layout]: https://api.reactrouter.com/v7/functions/_react-router_dev.routes.layout.html -[prefix]: https://api.reactrouter.com/v7/functions/_react-router_dev.routes.prefix.html -[relative]: https://api.reactrouter.com/v7/functions/_react-router_dev.routes.relative.html +[route]: https://api.reactrouter.com/v8/functions/_react-router_dev.routes.route.html +[index]: https://api.reactrouter.com/v8/functions/_react-router_dev.routes.index.html +[layout]: https://api.reactrouter.com/v8/functions/_react-router_dev.routes.layout.html +[prefix]: https://api.reactrouter.com/v8/functions/_react-router_dev.routes.prefix.html +[relative]: https://api.reactrouter.com/v8/functions/_react-router_dev.routes.relative.html [file-route-conventions]: ../../how-to/file-route-conventions diff --git a/docs/api/hooks/useFetcher.md b/docs/api/hooks/useFetcher.md index d70bf01543..c956ba1223 100644 --- a/docs/api/hooks/useFetcher.md +++ b/docs/api/hooks/useFetcher.md @@ -64,7 +64,7 @@ function useFetcher({ key, }: { key?: string; -} = ): FetcherWithComponents> {} +} = {}): FetcherWithComponents> ``` ## Params diff --git a/docs/api/hooks/useFormAction.md b/docs/api/hooks/useFormAction.md index 70f306db52..32e1830fb2 100644 --- a/docs/api/hooks/useFormAction.md +++ b/docs/api/hooks/useFormAction.md @@ -55,8 +55,8 @@ function useFormAction( relative, }: { relative?: RelativeRoutingType; - } = , -): string {} + } = {}, +): string ``` ## Params diff --git a/docs/api/hooks/useHref.md b/docs/api/hooks/useHref.md index bebe1a2378..a99a626e59 100644 --- a/docs/api/hooks/useHref.md +++ b/docs/api/hooks/useHref.md @@ -42,8 +42,8 @@ function useHref( relative, }: { relative?: RelativeRoutingType; - } = , -): string {} + } = {}, +): string ``` ## Params diff --git a/docs/api/hooks/useLinkClickHandler.md b/docs/api/hooks/useLinkClickHandler.md index 6f7a7a019d..a484f7682a 100644 --- a/docs/api/hooks/useLinkClickHandler.md +++ b/docs/api/hooks/useLinkClickHandler.md @@ -51,8 +51,8 @@ function useLinkClickHandler( viewTransition?: boolean; defaultShouldRevalidate?: boolean; useTransitions?: boolean; - } = , -): (event: React.MouseEvent) => void {} + } = {}, +): (event: React.MouseEvent) => void ``` ## Params diff --git a/docs/api/hooks/useResolvedPath.md b/docs/api/hooks/useResolvedPath.md index dd1cb54c69..07665b8eaa 100644 --- a/docs/api/hooks/useResolvedPath.md +++ b/docs/api/hooks/useResolvedPath.md @@ -47,8 +47,8 @@ function useResolvedPath( relative, }: { relative?: RelativeRoutingType; - } = , -): Path {} + } = {}, +): Path ``` ## Params diff --git a/docs/api/hooks/useViewTransitionState.md b/docs/api/hooks/useViewTransitionState.md index 8fd50bb5f6..766feb5762 100644 --- a/docs/api/hooks/useViewTransitionState.md +++ b/docs/api/hooks/useViewTransitionState.md @@ -38,8 +38,8 @@ function useViewTransitionState( relative, }: { relative?: RelativeRoutingType; - } = , -) {} + } = {}, +) ``` ## Params diff --git a/docs/api/other-api/adapter.md b/docs/api/other-api/adapter.md index 5a7251a118..6de19c9241 100644 --- a/docs/api/other-api/adapter.md +++ b/docs/api/other-api/adapter.md @@ -22,7 +22,7 @@ Each adapter has the same API. Some adapters also have options specific to the p ## `@react-router/express` -[Reference Documentation ↗](https://api.reactrouter.com/v7/modules/_react-router_express.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/modules/_react-router_express.html) Here's an example with [Express][express]: @@ -124,7 +124,7 @@ Make sure that `--conditions development` is included in the `dev` script so tha ## `@react-router/architect` -[Reference Documentation ↗](https://api.reactrouter.com/v7/modules/_react-router_architect.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/modules/_react-router_architect.html) Here's an example with Architect: @@ -139,7 +139,7 @@ export const handler = createRequestHandler({ ## `@react-router/cloudflare` -[Reference Documentation ↗](https://api.reactrouter.com/v7/modules/_react-router_cloudflare.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/modules/_react-router_cloudflare.html) Here's an example with Cloudflare: @@ -173,7 +173,7 @@ export default { While not a direct "adapter" like the above, this package contains utilities for working with Node-based adapters. -[Reference Documentation ↗](https://api.reactrouter.com/v7/modules/_react-router_node.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/modules/_react-router_node.html) ### Node Version Support diff --git a/docs/api/utils/IsCookieFunction.md b/docs/api/utils/IsCookieFunction.md index 5157a6d164..af2589e566 100644 --- a/docs/api/utils/IsCookieFunction.md +++ b/docs/api/utils/IsCookieFunction.md @@ -4,8 +4,41 @@ title: IsCookieFunction # IsCookieFunction + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.IsCookieFunction.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/types/react-router.IsCookieFunction.html) + +A function that determines whether a value is a React Router [`Cookie`](https://api.reactrouter.com/v8/interfaces/react-router.Cookie.html) +object. + +## Signature + +```tsx +type IsCookieFunction = (object: any) => object is Cookie; +``` + +## Params + +### object + +The value to check. + +## Returns + +`true` if the value is a React Router [`Cookie`](https://api.reactrouter.com/v8/interfaces/react-router.Cookie.html) object; +otherwise, `false`. + diff --git a/docs/api/utils/IsSessionFunction.md b/docs/api/utils/IsSessionFunction.md index bf3321cfe5..fde9cb9654 100644 --- a/docs/api/utils/IsSessionFunction.md +++ b/docs/api/utils/IsSessionFunction.md @@ -4,8 +4,41 @@ title: IsSessionFunction # IsSessionFunction + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.IsSessionFunction.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/types/react-router.IsSessionFunction.html) + +A function that determines whether a value is a React Router [`Session`](https://api.reactrouter.com/v8/interfaces/react-router.Session.html) +object. + +## Signature + +```tsx +type IsSessionFunction = (object: any) => object is Session; +``` + +## Params + +### object + +The value to check. + +## Returns + +`true` if the value is a React Router [`Session`](https://api.reactrouter.com/v8/interfaces/react-router.Session.html) object; +otherwise, `false`. + diff --git a/docs/api/utils/createCookie.md b/docs/api/utils/createCookie.md index 593dd558d9..66fa3100df 100644 --- a/docs/api/utils/createCookie.md +++ b/docs/api/utils/createCookie.md @@ -4,10 +4,37 @@ title: createCookie # createCookie + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createCookie.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.createCookie.html) Creates a logical container for managing a browser cookie from the server. + +## Params + +### name + +The name of the cookie. + +### cookieOptions + +Options for parsing and serializing the cookie. + +## Returns + +A [`Cookie`](https://api.reactrouter.com/v8/interfaces/react-router.Cookie.html) object for parsing and serializing the cookie. + diff --git a/docs/api/utils/createCookieSessionStorage.md b/docs/api/utils/createCookieSessionStorage.md index 83828fd1f4..78d5c2a93d 100644 --- a/docs/api/utils/createCookieSessionStorage.md +++ b/docs/api/utils/createCookieSessionStorage.md @@ -4,11 +4,23 @@ title: createCookieSessionStorage # createCookieSessionStorage + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createCookieSessionStorage.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.createCookieSessionStorage.html) Creates and returns a SessionStorage object that stores all session data directly in the session cookie itself. @@ -17,3 +29,23 @@ This has the advantage that no database or other backend services are needed, and can help to simplify some load-balanced scenarios. However, it also has the limitation that serialized session data may not exceed the browser's maximum cookie size. Trade-offs! + +## Signature + +```tsx +function createCookieSessionStorage({ + cookie: cookieArg, +}: CookieSessionStorageOptions = {}): SessionStorage +``` + +## Params + +### options + +Options for creating the cookie-backed session storage. + +## Returns + +A [`SessionStorage`](https://api.reactrouter.com/v8/interfaces/react-router.SessionStorage.html) object that stores all session data in its +cookie. + diff --git a/docs/api/utils/createMemorySessionStorage.md b/docs/api/utils/createMemorySessionStorage.md index 8d916894da..53cdb372f3 100644 --- a/docs/api/utils/createMemorySessionStorage.md +++ b/docs/api/utils/createMemorySessionStorage.md @@ -4,14 +4,44 @@ title: createMemorySessionStorage # createMemorySessionStorage + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createMemorySessionStorage.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.createMemorySessionStorage.html) + +Creates and returns a simple in-memory SessionStorage object. + +Intended for local development and testing. It does not scale beyond a single +process, and all session data is lost when the server process stops/restarts. + +## Signature + +```tsx +function createMemorySessionStorage({ + cookie, +}: MemorySessionStorageOptions = {}): SessionStorage +``` + +## Params + +### options + +Options for creating the in-memory session storage. + +## Returns -Creates and returns a simple in-memory SessionStorage object, mostly useful -for testing and as a reference implementation. +A [`SessionStorage`](https://api.reactrouter.com/v8/interfaces/react-router.SessionStorage.html) object that stores session data in memory. -Note: This storage does not scale beyond a single process, so it is not -suitable for most production scenarios. diff --git a/docs/api/utils/createPath.md b/docs/api/utils/createPath.md index 195141563d..e566a8287d 100644 --- a/docs/api/utils/createPath.md +++ b/docs/api/utils/createPath.md @@ -4,24 +4,43 @@ title: createPath # createPath + + [MODES: framework, data, declarative] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createPath.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.createPath.html) Creates a string URL path from the given pathname, search, and hash components. ## Signature ```tsx -createPath(__namedParameters): string +function createPath({ + pathname = "/", + search = "", + hash = "", +}: Partial) ``` ## Params -### \_\_namedParameters +### path + +The pathname, search, and hash components to combine. + +## Returns -[modes: framework, data, declarative] +The combined URL path. -_No documentation_ diff --git a/docs/api/utils/createRequestHandler.md b/docs/api/utils/createRequestHandler.md deleted file mode 100644 index b85ba28ec5..0000000000 --- a/docs/api/utils/createRequestHandler.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: createRequestHandler -hidden: true ---- - -# createRequestHandler - -[MODES: framework, data, declarative] - -## Summary - -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createRequestHandler.html) diff --git a/docs/api/utils/createRoutesStub.md b/docs/api/utils/createRoutesStub.md index e707fb6555..4d11d903cd 100644 --- a/docs/api/utils/createRoutesStub.md +++ b/docs/api/utils/createRoutesStub.md @@ -4,28 +4,50 @@ title: createRoutesStub # createRoutesStub + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createRoutesStub.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.createRoutesStub.html) + +Creates a React component that renders the provided routes in a test-friendly +React Router context. + +Use this to unit test components that rely on router context, such as +`loaderData`, `actionData`, and route matches. ## Signature ```tsx -createRoutesStub(routes, context): undefined +function createRoutesStub( + routes: StubRouteObject[], + _context?: RouterContextProvider, +) ``` ## Params ### routes -[modes: framework, data] +The route objects to render in the test router. + +### _context -_No documentation_ +An optional [`RouterContextProvider`](../utils/RouterContextProvider) for supplying application context values to route middleware, loaders, and actions. -### context +## Returns -[modes: framework, data] +A React component that renders the test router. -_No documentation_ diff --git a/docs/api/utils/createSearchParams.md b/docs/api/utils/createSearchParams.md index b0603e8f5e..18ff59d1c8 100644 --- a/docs/api/utils/createSearchParams.md +++ b/docs/api/utils/createSearchParams.md @@ -4,46 +4,58 @@ title: createSearchParams # createSearchParams + + [MODES: framework, data, declarative] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createSearchParams.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.createSearchParams.html) Creates a URLSearchParams object using the given initializer. -This is identical to `new URLSearchParams(init)` except it also -supports arrays as values in the object form of the initializer -instead of just strings. This is convenient when you need multiple -values for a given key, but don't want to use an array initializer. - -For example, instead of: +This is identical to `new URLSearchParams(init)` except it also supports +arrays as values in the object form of the initializer instead of just +strings. This is convenient when you need multiple values for a given key, +but don't want to use an array initializer. ```tsx +// Instead of: let searchParams = new URLSearchParams([ ["sort", "name"], ["sort", "price"], ]); -``` - -you can do: -``` +// You can do: let searchParams = createSearchParams({ - sort: ['name', 'price'] + sort: ["name", "price"], }); ``` ## Signature ```tsx -createSearchParams(init): URLSearchParams +function createSearchParams(init: URLSearchParamsInit = ""): URLSearchParams ``` ## Params ### init -[modes: framework, data, declarative] +The value used to initialize the URL search parameters. + +## Returns + +A URLSearchParams object containing the initialized search +parameters. -_No documentation_ diff --git a/docs/api/utils/createSession.md b/docs/api/utils/createSession.md deleted file mode 100644 index 37d738b705..0000000000 --- a/docs/api/utils/createSession.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: createSession -hidden: true ---- - -# createSession - -[MODES: framework, data, declarative] - -## Summary - -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createSession.html) - -Creates a new Session object. - -Note: This function is typically not invoked directly by application code. -Instead, use a `SessionStorage` object's `getSession` method. diff --git a/docs/api/utils/createSessionStorage.md b/docs/api/utils/createSessionStorage.md deleted file mode 100644 index 29a1b62352..0000000000 --- a/docs/api/utils/createSessionStorage.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: createSessionStorage -hidden: true ---- - -# createSessionStorage - -[MODES: framework, data, declarative] - -## Summary - -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.createSessionStorage.html) - -Creates a SessionStorage object using a SessionIdStorageStrategy. - -Note: This is a low-level API that should only be used if none of the -existing session storage options meet your requirements. diff --git a/docs/api/utils/generatePath.md b/docs/api/utils/generatePath.md index 0bc46bab31..dc70034e1f 100644 --- a/docs/api/utils/generatePath.md +++ b/docs/api/utils/generatePath.md @@ -47,8 +47,8 @@ generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1" ```tsx function generatePath( originalPath: Path, - params: GeneratePathParams = as any, -): string {} + params: GeneratePathParams = {} as any, +): string ``` ## Params diff --git a/docs/api/utils/isCookie.md b/docs/api/utils/isCookie.md index ac0f3b3f32..b74c44d2a0 100644 --- a/docs/api/utils/isCookie.md +++ b/docs/api/utils/isCookie.md @@ -4,10 +4,34 @@ title: isCookie # isCookie + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.isCookie.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/variables/react-router.isCookie.html) + +Returns `true` if a value is a React Router [`Cookie`](https://api.reactrouter.com/v8/interfaces/react-router.Cookie.html) object. + +## Params + +### object + +The value to check. + +## Returns + +`true` if the value is a React Router [`Cookie`](https://api.reactrouter.com/v8/interfaces/react-router.Cookie.html) object; +otherwise, `false`. -Returns true if an object is a Remix cookie container. diff --git a/docs/api/utils/isSession.md b/docs/api/utils/isSession.md index ebac237abc..9842a116ca 100644 --- a/docs/api/utils/isSession.md +++ b/docs/api/utils/isSession.md @@ -4,10 +4,34 @@ title: isSession # isSession + + [MODES: framework, data] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.isSession.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/variables/react-router.isSession.html) + +Returns `true` if a value is a React Router [`Session`](https://api.reactrouter.com/v8/interfaces/react-router.Session.html) object. + +## Params + +### object + +The value to check. + +## Returns + +`true` if the value is a React Router [`Session`](https://api.reactrouter.com/v8/interfaces/react-router.Session.html) object; +otherwise, `false`. -Returns true if an object is a React Router session. diff --git a/docs/api/utils/parsePath.md b/docs/api/utils/parsePath.md index bb1c485cfe..1cb7aa4a68 100644 --- a/docs/api/utils/parsePath.md +++ b/docs/api/utils/parsePath.md @@ -4,24 +4,39 @@ title: parsePath # parsePath + + [MODES: framework, data, declarative] ## Summary -[Reference Documentation ↗](https://api.reactrouter.com/v7/functions/react-router.parsePath.html) +[Reference Documentation ↗](https://api.reactrouter.com/v8/functions/react-router.parsePath.html) Parses a string URL path into its separate pathname, search, and hash components. ## Signature ```tsx -parsePath(path): Partial +function parsePath(path: string): Partial ``` ## Params ### path -[modes: framework, data, declarative] +The URL path to parse. + +## Returns + +The parsed pathname, search, and hash components. -_No documentation_ diff --git a/docs/explanation/sessions-and-cookies.md b/docs/explanation/sessions-and-cookies.md index dacf9df18a..5cd685deb5 100644 --- a/docs/explanation/sessions-and-cookies.md +++ b/docs/explanation/sessions-and-cookies.md @@ -449,17 +449,17 @@ To learn more about each attribute, please see the [MDN Set-Cookie docs][cookie- [csrf]: https://developer.mozilla.org/en-US/docs/Glossary/CSRF [cookies]: #cookies [sessions]: #sessions -[session-storage]: https://api.reactrouter.com/v7/interfaces/react-router.SessionStorage -[session-api]: https://api.reactrouter.com/v7/interfaces/react-router.Session -[is-session]: https://api.reactrouter.com/v7/functions/react-router.isSession -[cookie-api]: https://api.reactrouter.com/v7/interfaces/react-router.Cookie -[create-session-storage]: https://api.reactrouter.com/v7/functions/react-router.createSessionStorage -[create-session]: https://api.reactrouter.com/v7/functions/react-router.createSession -[create-memory-session-storage]: https://api.reactrouter.com/v7/functions/react-router.createMemorySessionStorage -[create-file-session-storage]: https://api.reactrouter.com/v7/functions/_react-router_node.createFileSessionStorage -[create-workers-kv-session-storage]: https://api.reactrouter.com/v7/functions/_react-router_cloudflare.createWorkersKVSessionStorage -[create-arc-table-session-storage]: https://api.reactrouter.com/v7/functions/_react-router_architect.createArcTableSessionStorage +[session-storage]: https://api.reactrouter.com/v8/interfaces/react-router.SessionStorage +[session-api]: https://api.reactrouter.com/v8/interfaces/react-router.Session +[is-session]: https://api.reactrouter.com/v8/variables/react-router.isSession.html +[cookie-api]: https://api.reactrouter.com/v8/interfaces/react-router.Cookie +[create-session-storage]: https://api.reactrouter.com/v8/functions/react-router.createSessionStorage +[create-session]: https://api.reactrouter.com/v8/variables/react-router.createSession.html +[create-memory-session-storage]: https://api.reactrouter.com/v8/functions/react-router.createMemorySessionStorage +[create-file-session-storage]: https://api.reactrouter.com/v8/functions/_react-router_node.createFileSessionStorage +[create-workers-kv-session-storage]: https://api.reactrouter.com/v8/functions/_react-router_cloudflare.createWorkersKVSessionStorage +[create-arc-table-session-storage]: https://api.reactrouter.com/v8/functions/_react-router_architect.createArcTableSessionStorage [cookie]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies [cookie-attrs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes -[is-cookie]: https://api.reactrouter.com/v7/functions/react-router.isCookie -[create-cookie]: https://api.reactrouter.com/v7/functions/react-router.createCookie +[is-cookie]: https://api.reactrouter.com/v8/variables/react-router.isCookie.html +[create-cookie]: https://api.reactrouter.com/v8/functions/react-router.createCookie diff --git a/docs/explanation/state-management.md b/docs/explanation/state-management.md index c2b0304e99..07943f9c3e 100644 --- a/docs/explanation/state-management.md +++ b/docs/explanation/state-management.md @@ -510,8 +510,8 @@ If you ever find yourself entangled in managing and synchronizing state for netw [redux]: https://redux.js.org/ [tanstack_query]: https://tanstack.com/query/latest [apollo]: https://www.apollographql.com/ -[use_navigation]: https://api.reactrouter.com/v7/functions/react-router.useNavigation -[use_fetcher]: https://api.reactrouter.com/v7/functions/react-router.useFetcher +[use_navigation]: https://api.reactrouter.com/v8/functions/react-router.useNavigation +[use_fetcher]: https://api.reactrouter.com/v8/functions/react-router.useFetcher [loader_data]: ../start/framework/data-loading [action_data]: ../start/framework/actions [cookies]: ./sessions-and-cookies#cookies diff --git a/docs/how-to/presets.md b/docs/how-to/presets.md index febf65418c..10145fd50a 100644 --- a/docs/how-to/presets.md +++ b/docs/how-to/presets.md @@ -98,6 +98,6 @@ export default { } satisfies Config; ``` -[react-router-config]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Config.html -[preset-type]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Preset.html +[react-router-config]: https://api.reactrouter.com/v8/types/_react-router_dev.config.Config.html +[preset-type]: https://api.reactrouter.com/v8/types/_react-router_dev.config.Preset.html [server-bundles]: ./server-bundles diff --git a/docs/how-to/server-bundles.md b/docs/how-to/server-bundles.md index 7c4a3a09db..f58a437ffd 100644 --- a/docs/how-to/server-bundles.md +++ b/docs/how-to/server-bundles.md @@ -62,5 +62,5 @@ When using server bundles, the build manifest contains the following properties: - `routeIdToServerBundleId` — An object that maps route IDs to their server bundle ID - `routes` — A route manifest that maps route IDs to route metadata. This can be used to drive a custom routing layer in front of your React Router request handlers -[react-router-config]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Config.html -[server-bundles-function]: https://api.reactrouter.com/v7/types/_react-router_dev.config.ServerBundlesFunction.html +[react-router-config]: https://api.reactrouter.com/v8/types/_react-router_dev.config.Config.html +[server-bundles-function]: https://api.reactrouter.com/v8/types/_react-router_dev.config.ServerBundlesFunction.html diff --git a/docs/start/data/route-object.md b/docs/start/data/route-object.md index 0a9592caef..b26b088719 100644 --- a/docs/start/data/route-object.md +++ b/docs/start/data/route-object.md @@ -216,7 +216,7 @@ createBrowserRouter([ ]); ``` -[`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/react-router.ShouldRevalidateFunctionArgs.html) +[`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v8/interfaces/react-router.ShouldRevalidateFunctionArgs.html) Please note the default behavior is different in [Framework Mode](../modes). @@ -263,6 +263,6 @@ See also: Next: [Data Loading](./data-loading) -[loader-params]: https://api.reactrouter.com/v7/interfaces/react-router.LoaderFunctionArgs +[loader-params]: https://api.reactrouter.com/v8/interfaces/react-router.LoaderFunctionArgs [middleware]: ../../how-to/middleware [use-matches]: ../../api/hooks/useMatches diff --git a/docs/start/data/routing.md b/docs/start/data/routing.md index 5365a14425..67f777b72e 100644 --- a/docs/start/data/routing.md +++ b/docs/start/data/routing.md @@ -278,4 +278,4 @@ const { "*": splat } = params; Next: [Route Object](./route-object) -[outlet]: https://api.reactrouter.com/v7/functions/react-router.Outlet.html +[outlet]: https://api.reactrouter.com/v8/functions/react-router.Outlet.html diff --git a/docs/start/framework/pending-ui.md b/docs/start/framework/pending-ui.md index 24e107397f..5d5d949ccb 100644 --- a/docs/start/framework/pending-ui.md +++ b/docs/start/framework/pending-ui.md @@ -139,4 +139,4 @@ function Task({ task }) { Next: [Testing](./testing) -[use_fetcher]: https://api.reactrouter.com/v7/functions/react-router.useFetcher.html +[use_fetcher]: https://api.reactrouter.com/v8/functions/react-router.useFetcher.html diff --git a/docs/start/framework/route-module.md b/docs/start/framework/route-module.md index fe780e6773..e2ac6206ef 100644 --- a/docs/start/framework/route-module.md +++ b/docs/start/framework/route-module.md @@ -500,27 +500,27 @@ export function shouldRevalidate( When using [SPA Mode][spa-mode], there are no server loaders to call on navigations, so `shouldRevalidate` behaves the same as it does in [Data Mode][data-mode-should-revalidate]. -[`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/react-router.ShouldRevalidateFunctionArgs.html) +[`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v8/interfaces/react-router.ShouldRevalidateFunctionArgs.html) --- Next: [Rendering Strategies](./rendering) -[middleware-params]: https://api.reactrouter.com/v7/types/react-router.MiddlewareFunction.html +[middleware-params]: https://api.reactrouter.com/v8/types/react-router.MiddlewareFunction.html [middleware]: ../../how-to/middleware [when-middleware-runs]: ../../how-to/middleware#when-middleware-runs -[loader-params]: https://api.reactrouter.com/v7/interfaces/react-router.LoaderFunctionArgs -[client-loader-params]: https://api.reactrouter.com/v7/types/react-router.ClientLoaderFunctionArgs -[action-params]: https://api.reactrouter.com/v7/interfaces/react-router.ActionFunctionArgs -[client-action-params]: https://api.reactrouter.com/v7/types/react-router.ClientActionFunctionArgs +[loader-params]: https://api.reactrouter.com/v8/interfaces/react-router.LoaderFunctionArgs +[client-loader-params]: https://api.reactrouter.com/v8/types/react-router.ClientLoaderFunctionArgs +[action-params]: https://api.reactrouter.com/v8/interfaces/react-router.ActionFunctionArgs +[client-action-params]: https://api.reactrouter.com/v8/types/react-router.ClientActionFunctionArgs [use-route-error]: ../../api/hooks/useRouteError [is-route-error-response]: ../../api/utils/isRouteErrorResponse [headers]: https://developer.mozilla.org/en-US/docs/Web/API/Response/headers [use-matches]: ../../api/hooks/useMatches [link-element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link [meta-element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta -[meta-params]: https://api.reactrouter.com/v7/interfaces/react-router.MetaArgs -[meta-function]: https://api.reactrouter.com/v7/types/react-router.MetaDescriptor.html +[meta-params]: https://api.reactrouter.com/v8/interfaces/react-router.MetaArgs +[meta-function]: https://api.reactrouter.com/v8/types/react-router.MetaDescriptor.html [data-mode-should-revalidate]: ../data/route-object#shouldrevalidate [spa-mode]: ../../how-to/spa [client-data]: ../../how-to/client-data diff --git a/docs/start/framework/routing.md b/docs/start/framework/routing.md index 73845a8d63..982bbfea2a 100644 --- a/docs/start/framework/routing.md +++ b/docs/start/framework/routing.md @@ -359,4 +359,4 @@ Note that these routes do not participate in data loading, actions, code splitti Next: [Route Module](./route-module) [file-route-conventions]: ../../how-to/file-route-conventions -[outlet]: https://api.reactrouter.com/v7/functions/react-router.Outlet.html +[outlet]: https://api.reactrouter.com/v8/functions/react-router.Outlet.html diff --git a/docs/tutorials/address-book.md b/docs/tutorials/address-book.md index ac47c49926..e1417315e0 100644 --- a/docs/tutorials/address-book.md +++ b/docs/tutorials/address-book.md @@ -1942,10 +1942,10 @@ That's it! Thanks for giving React Router a shot. We hope this tutorial gives yo [root-route]: ../api/framework-conventions/root.tsx [error-boundaries]: ../how-to/error-boundary [links]: ../start/framework/route-module#links -[outlet-component]: https://api.reactrouter.com/v7/functions/react-router.Outlet +[outlet-component]: https://api.reactrouter.com/v8/functions/react-router.Outlet [file-route-conventions]: ../how-to/file-route-conventions [contacts-1]: http://localhost:5173/contacts/1 -[link-component]: https://api.reactrouter.com/v7/functions/react-router.Link +[link-component]: https://api.reactrouter.com/v8/variables/react-router.Link.html [client-loader]: ../start/framework/route-module#clientloader [spa]: ../how-to/spa [type-safety]: ../explanation/type-safety @@ -1959,17 +1959,17 @@ That's it! Thanks for giving React Router a shot. We hope this tutorial gives yo [url-search-params]: https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams [loader]: ../start/framework/route-module#loader [action]: ../start/framework/route-module#action -[form-component]: https://api.reactrouter.com/v7/functions/react-router.Form +[form-component]: https://api.reactrouter.com/v8/variables/react-router.Form.html [fetch]: https://developer.mozilla.org/en-US/docs/Web/API/fetch [form-data]: https://developer.mozilla.org/en-US/docs/Web/API/FormData [object-from-entries]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries [request-form-data]: https://developer.mozilla.org/en-US/docs/Web/API/Request/formData [request]: https://developer.mozilla.org/en-US/docs/Web/API/Request -[redirect]: https://api.reactrouter.com/v7/functions/react-router.redirect +[redirect]: https://api.reactrouter.com/v8/variables/react-router.redirect.html [response]: https://developer.mozilla.org/en-US/docs/Web/API/Response -[nav-link]: https://api.reactrouter.com/v7/functions/react-router.NavLink -[use-navigation]: https://api.reactrouter.com/v7/functions/react-router.useNavigation -[use-navigate]: https://api.reactrouter.com/v7/functions/react-router.useNavigate -[use-submit]: https://api.reactrouter.com/v7/functions/react-router.useSubmit -[use-fetcher]: https://api.reactrouter.com/v7/functions/react-router.useFetcher -[react-router-apis]: https://api.reactrouter.com/v7/modules/react_router +[nav-link]: https://api.reactrouter.com/v8/variables/react-router.NavLink.html +[use-navigation]: https://api.reactrouter.com/v8/functions/react-router.useNavigation +[use-navigate]: https://api.reactrouter.com/v8/functions/react-router.useNavigate +[use-submit]: https://api.reactrouter.com/v8/functions/react-router.useSubmit +[use-fetcher]: https://api.reactrouter.com/v8/functions/react-router.useFetcher +[react-router-apis]: https://api.reactrouter.com/v8/modules/react-router.html diff --git a/docs/tutorials/quickstart.md b/docs/tutorials/quickstart.md index 6f52add340..5e05a18882 100644 --- a/docs/tutorials/quickstart.md +++ b/docs/tutorials/quickstart.md @@ -294,7 +294,7 @@ What's next? [routing]: ../start/framework/routing [http-localhost-3000]: http://localhost:3000 [vite]: https://vitejs.dev -[react-router-config]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Config.html +[react-router-config]: https://api.reactrouter.com/v8/types/_react-router_dev.config.Config.html [vite-middleware]: https://vitejs.dev/guide/ssr#setting-up-the-dev-server [cross-env]: https://www.npmjs.com/package/cross-env [address-book-tutorial]: ./address-book diff --git a/packages/react-router/lib/dom/dom.ts b/packages/react-router/lib/dom/dom.ts index 5a84f3ca7a..73e53a4c39 100644 --- a/packages/react-router/lib/dom/dom.ts +++ b/packages/react-router/lib/dom/dom.ts @@ -51,30 +51,30 @@ export type URLSearchParamsInit = | URLSearchParams; /** - Creates a URLSearchParams object using the given initializer. - - This is identical to `new URLSearchParams(init)` except it also - supports arrays as values in the object form of the initializer - instead of just strings. This is convenient when you need multiple - values for a given key, but don't want to use an array initializer. - - For example, instead of: - - ```tsx - let searchParams = new URLSearchParams([ - ['sort', 'name'], - ['sort', 'price'] - ]); - ``` - you can do: - - ``` - let searchParams = createSearchParams({ - sort: ['name', 'price'] - }); - ``` - - @category Utils + * Creates a URLSearchParams object using the given initializer. + * + * This is identical to `new URLSearchParams(init)` except it also supports + * arrays as values in the object form of the initializer instead of just + * strings. This is convenient when you need multiple values for a given key, + * but don't want to use an array initializer. + * + * @example + * // Instead of: + * let searchParams = new URLSearchParams([ + * ["sort", "name"], + * ["sort", "price"], + * ]); + * + * // You can do: + * let searchParams = createSearchParams({ + * sort: ["name", "price"], + * }); + * + * @public + * @category Utils + * @param init The value used to initialize the URL search parameters. + * @returns A URLSearchParams object containing the initialized search + * parameters. */ export function createSearchParams( init: URLSearchParamsInit = "", diff --git a/packages/react-router/lib/dom/ssr/routes-test-stub.tsx b/packages/react-router/lib/dom/ssr/routes-test-stub.tsx index 9b0f707838..ca26504d17 100644 --- a/packages/react-router/lib/dom/ssr/routes-test-stub.tsx +++ b/packages/react-router/lib/dom/ssr/routes-test-stub.tsx @@ -109,7 +109,20 @@ export interface RoutesTestStubProps { } /** + * Creates a React component that renders the provided routes in a test-friendly + * React Router context. + * + * Use this to unit test components that rely on router context, such as + * `loaderData`, `actionData`, and route matches. + * + * @public * @category Utils + * @mode framework + * @mode data + * @param routes The route objects to render in the test router. + * @param _context An optional {@link RouterContextProvider} for supplying + * application context values to route middleware, loaders, and actions. + * @returns A React component that renders the test router. */ export function createRoutesStub( routes: StubRouteObject[], diff --git a/packages/react-router/lib/router/history.ts b/packages/react-router/lib/router/history.ts index 5c6a72b438..559ea24382 100644 --- a/packages/react-router/lib/router/history.ts +++ b/packages/react-router/lib/router/history.ts @@ -596,7 +596,10 @@ export function createLocation( /** * Creates a string URL path from the given pathname, search, and hash components. * + * @public * @category Utils + * @param path The pathname, search, and hash components to combine. + * @returns The combined URL path. */ export function createPath({ pathname = "/", @@ -613,7 +616,10 @@ export function createPath({ /** * Parses a string URL path into its separate pathname, search, and hash components. * + * @public * @category Utils + * @param path The URL path to parse. + * @returns The parsed pathname, search, and hash components. */ export function parsePath(path: string): Partial { let parsedPath: Partial = {}; diff --git a/packages/react-router/lib/server-runtime/cookies.ts b/packages/react-router/lib/server-runtime/cookies.ts index dad5833177..3a3fa29346 100644 --- a/packages/react-router/lib/server-runtime/cookies.ts +++ b/packages/react-router/lib/server-runtime/cookies.ts @@ -69,6 +69,14 @@ export interface Cookie { /** * Creates a logical container for managing a browser cookie from the server. + * + * @public + * @category Utils + * @mode framework + * @mode data + * @param name The name of the cookie. + * @param cookieOptions Options for parsing and serializing the cookie. + * @returns A {@link Cookie} object for parsing and serializing the cookie. */ export const createCookie = ( name: string, @@ -123,12 +131,30 @@ export const createCookie = ( }; }; +/** + * A function that determines whether a value is a React Router {@link Cookie} + * object. + * + * @public + * @category Utils + * @mode framework + * @mode data + * @param object The value to check. + * @returns `true` if the value is a React Router {@link Cookie} object; + * otherwise, `false`. + */ export type IsCookieFunction = (object: any) => object is Cookie; /** - * Returns true if an object is a Remix cookie container. + * Returns `true` if a value is a React Router {@link Cookie} object. * - * @see https://remix.run/utils/cookies#iscookie + * @public + * @category Utils + * @mode framework + * @mode data + * @param object The value to check. + * @returns `true` if the value is a React Router {@link Cookie} object; + * otherwise, `false`. */ export const isCookie: IsCookieFunction = (object): object is Cookie => { return ( diff --git a/packages/react-router/lib/server-runtime/server.ts b/packages/react-router/lib/server-runtime/server.ts index 91edf2c315..e1373fe4e3 100644 --- a/packages/react-router/lib/server-runtime/server.ts +++ b/packages/react-router/lib/server-runtime/server.ts @@ -325,6 +325,18 @@ function derive(build: ServerBuild, mode?: string) { }; } +/** + * Creates a request handler for a React Router server build. + * + * This is a low-level API used by server adapters to translate incoming + * requests into React Router responses. + * + * @category Utils + * @param build The server build, or a function that resolves to the server + * build, used to handle requests. + * @param mode The mode in which the server build is running. + * @returns A request handler that returns a response for each incoming request. + */ export const createRequestHandler: CreateRequestHandlerFunction = ( build, mode, diff --git a/packages/react-router/lib/server-runtime/sessions.ts b/packages/react-router/lib/server-runtime/sessions.ts index d8b9beaafd..615e23fbff 100644 --- a/packages/react-router/lib/server-runtime/sessions.ts +++ b/packages/react-router/lib/server-runtime/sessions.ts @@ -89,6 +89,12 @@ export type CreateSessionFunction = ( * * Note: This function is typically not invoked directly by application code. * Instead, use a `SessionStorage` object's `getSession` method. + * + * @category Utils + * @param initialData The initial data for the session. + * @param id The identifier for the session. Defaults to an empty string for a + * new session. + * @returns A new {@link Session} object. */ export const createSession: CreateSessionFunction = < Data = SessionData, @@ -139,12 +145,30 @@ export const createSession: CreateSessionFunction = < }; }; +/** + * A function that determines whether a value is a React Router {@link Session} + * object. + * + * @public + * @category Utils + * @mode framework + * @mode data + * @param object The value to check. + * @returns `true` if the value is a React Router {@link Session} object; + * otherwise, `false`. + */ export type IsSessionFunction = (object: any) => object is Session; /** - * Returns true if an object is a React Router session. + * Returns `true` if a value is a React Router {@link Session} object. * - * @see https://reactrouter.com/api/utils/isSession + * @public + * @category Utils + * @mode framework + * @mode data + * @param object The value to check. + * @returns `true` if the value is a React Router {@link Session} object; + * otherwise, `false`. */ export const isSession: IsSessionFunction = (object): object is Session => { return ( @@ -248,6 +272,11 @@ export interface SessionIdStorageStrategy< * * Note: This is a low-level API that should only be used if none of the * existing session storage options meet your requirements. + * + * @category Utils + * @param strategy The strategy used to store session identifiers and data. + * @returns A {@link SessionStorage} object that persists session data using the + * provided strategy. */ export function createSessionStorage({ cookie: cookieArg, diff --git a/packages/react-router/lib/server-runtime/sessions/cookieStorage.ts b/packages/react-router/lib/server-runtime/sessions/cookieStorage.ts index 4059e495d0..776a157a8b 100644 --- a/packages/react-router/lib/server-runtime/sessions/cookieStorage.ts +++ b/packages/react-router/lib/server-runtime/sessions/cookieStorage.ts @@ -22,6 +22,14 @@ interface CookieSessionStorageOptions { * needed, and can help to simplify some load-balanced scenarios. However, it * also has the limitation that serialized session data may not exceed the * browser's maximum cookie size. Trade-offs! + * + * @public + * @category Utils + * @mode framework + * @mode data + * @param options Options for creating the cookie-backed session storage. + * @returns A {@link SessionStorage} object that stores all session data in its + * cookie. */ export function createCookieSessionStorage< Data = SessionData, diff --git a/packages/react-router/lib/server-runtime/sessions/memoryStorage.ts b/packages/react-router/lib/server-runtime/sessions/memoryStorage.ts index d39ea62c7a..f748466766 100644 --- a/packages/react-router/lib/server-runtime/sessions/memoryStorage.ts +++ b/packages/react-router/lib/server-runtime/sessions/memoryStorage.ts @@ -19,6 +19,13 @@ interface MemorySessionStorageOptions { * * Intended for local development and testing. It does not scale beyond a single * process, and all session data is lost when the server process stops/restarts. + * + * @public + * @category Utils + * @mode framework + * @mode data + * @param options Options for creating the in-memory session storage. + * @returns A {@link SessionStorage} object that stores session data in memory. */ export function createMemorySessionStorage< Data = SessionData, diff --git a/scripts/docs.ts b/scripts/docs.ts index f5db20ec5b..f07f011997 100644 --- a/scripts/docs.ts +++ b/scripts/docs.ts @@ -570,6 +570,11 @@ function getApiName(comment: ParsedComment): string { return matches[1].trim(); } + matches = comment.code.match(/^export type ([^<=]+)/); + if (matches) { + return matches[1].trim(); + } + throw new Error(`Could not determine API name:\n${comment.code}\n`); } @@ -713,7 +718,7 @@ async function getSignature(code: string) { let formatted = await prettier.format(newCode, { parser: "typescript" }); - return formatted.replace("{}", "").trim(); + return formatted.replace(/\s*\{\}\s*$/, "").trim(); } // TODO: Handle variable statements for forwardRef components @@ -731,6 +736,22 @@ async function getSignature(code: string) { return; } + if (ts.isTypeAliasDeclaration(ast.statements[0])) { + let typeAliasDeclaration = ast.statements[0]; + let modifiedTypeAlias = { + ...typeAliasDeclaration, + modifiers: typeAliasDeclaration.modifiers?.filter( + (m) => m.kind !== ts.SyntaxKind.ExportKeyword, + ), + } as ts.TypeAliasDeclaration; + + let newCode = ts + .createPrinter({ newLine: ts.NewLineKind.LineFeed }) + .printNode(ts.EmitHint.Unspecified, modifiedTypeAlias, ast); + + return (await prettier.format(newCode, { parser: "typescript" })).trim(); + } + throw new Error("Unable to parse signature from code: " + code); }