diff --git a/CHANGELOG.md b/CHANGELOG.md index 458e0446f9..a2a1a00b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,86 +13,88 @@ We manage release notes in this file instead of the paginated Github Releases Pa Table of Contents - [React Router Releases](#react-router-releases) + - [v7.9.3](#v793) + - [Patch Changes](#patch-changes) - [v7.9.2](#v792) - [What's Changed](#whats-changed) - [RSC Framework Mode (unstable)](#rsc-framework-mode-unstable) - [Fetcher Reset (unstable)](#fetcher-reset-unstable) - - [Patch Changes](#patch-changes) + - [Patch Changes](#patch-changes-1) - [Unstable Changes](#unstable-changes) - [v7.9.1](#v791) - - [Patch Changes](#patch-changes-1) + - [Patch Changes](#patch-changes-2) - [v7.9.0](#v790) - [What's Changed](#whats-changed-1) - [Stable Middleware and Context APIs](#stable-middleware-and-context-apis) - [Minor Changes](#minor-changes) - - [Patch Changes](#patch-changes-2) + - [Patch Changes](#patch-changes-3) - [Unstable Changes](#unstable-changes-1) - [v7.8.2](#v782) - - [Patch Changes](#patch-changes-3) + - [Patch Changes](#patch-changes-4) - [Unstable Changes](#unstable-changes-2) - [v7.8.1](#v781) - - [Patch Changes](#patch-changes-4) + - [Patch Changes](#patch-changes-5) - [Unstable Changes](#unstable-changes-3) - [v7.8.0](#v780) - [What's Changed](#whats-changed-2) - [Consistently named `loaderData` values](#consistently-named-loaderdata-values) - [Improvements/fixes to the middleware APIs (unstable)](#improvementsfixes-to-the-middleware-apis-unstable) - [Minor Changes](#minor-changes-1) - - [Patch Changes](#patch-changes-5) + - [Patch Changes](#patch-changes-6) - [Unstable Changes](#unstable-changes-4) - [Changes by Package](#changes-by-package) - [v7.7.1](#v771) - - [Patch Changes](#patch-changes-6) + - [Patch Changes](#patch-changes-7) - [Unstable Changes](#unstable-changes-5) - [v7.7.0](#v770) - [What's Changed](#whats-changed-3) - [Unstable RSC APIs](#unstable-rsc-apis) - [Minor Changes](#minor-changes-2) - - [Patch Changes](#patch-changes-7) + - [Patch Changes](#patch-changes-8) - [Unstable Changes](#unstable-changes-6) - [Changes by Package](#changes-by-package-1) - [v7.6.3](#v763) - - [Patch Changes](#patch-changes-8) - - [v7.6.2](#v762) - [Patch Changes](#patch-changes-9) - - [v7.6.1](#v761) + - [v7.6.2](#v762) - [Patch Changes](#patch-changes-10) + - [v7.6.1](#v761) + - [Patch Changes](#patch-changes-11) - [Unstable Changes](#unstable-changes-7) - [v7.6.0](#v760) - [What's Changed](#whats-changed-4) - [`routeDiscovery` Config Option](#routediscovery-config-option) - [Automatic Types for Future Flags](#automatic-types-for-future-flags) - [Minor Changes](#minor-changes-3) - - [Patch Changes](#patch-changes-11) + - [Patch Changes](#patch-changes-12) - [Unstable Changes](#unstable-changes-8) - [Changes by Package](#changes-by-package-2) - [v7.5.3](#v753) - - [Patch Changes](#patch-changes-12) + - [Patch Changes](#patch-changes-13) - [v7.5.2](#v752) - [Security Notice](#security-notice) - - [Patch Changes](#patch-changes-13) - - [v7.5.1](#v751) - [Patch Changes](#patch-changes-14) + - [v7.5.1](#v751) + - [Patch Changes](#patch-changes-15) - [Unstable Changes](#unstable-changes-9) - [v7.5.0](#v750) - [What's Changed](#whats-changed-5) - [`route.lazy` Object API](#routelazy-object-api) - [Minor Changes](#minor-changes-4) - - [Patch Changes](#patch-changes-15) + - [Patch Changes](#patch-changes-16) - [Unstable Changes](#unstable-changes-10) - [Changes by Package](#changes-by-package-3) - [v7.4.1](#v741) - [Security Notice](#security-notice-1) - - [Patch Changes](#patch-changes-16) + - [Patch Changes](#patch-changes-17) - [Unstable Changes](#unstable-changes-11) - [v7.4.0](#v740) - [Minor Changes](#minor-changes-5) - - [Patch Changes](#patch-changes-17) + - [Patch Changes](#patch-changes-18) - [Unstable Changes](#unstable-changes-12) - [Changes by Package](#changes-by-package-4) - [v7.3.0](#v730) - [Minor Changes](#minor-changes-6) - - [Patch Changes](#patch-changes-18) + - [Patch Changes](#patch-changes-19) - [Unstable Changes](#unstable-changes-13) - [Client-side `context` (unstable)](#client-side-context-unstable) - [Middleware (unstable)](#middleware-unstable) @@ -105,28 +107,28 @@ We manage release notes in this file instead of the paginated Github Releases Pa - [Prerendering with a SPA Fallback](#prerendering-with-a-spa-fallback) - [Allow a root `loader` in SPA Mode](#allow-a-root-loader-in-spa-mode) - [Minor Changes](#minor-changes-7) - - [Patch Changes](#patch-changes-19) + - [Patch Changes](#patch-changes-20) - [Unstable Changes](#unstable-changes-14) - [Split Route Modules (unstable)](#split-route-modules-unstable) - [Changes by Package](#changes-by-package-6) - [v7.1.5](#v715) - - [Patch Changes](#patch-changes-20) - - [v7.1.4](#v714) - [Patch Changes](#patch-changes-21) - - [v7.1.3](#v713) + - [v7.1.4](#v714) - [Patch Changes](#patch-changes-22) - - [v7.1.2](#v712) + - [v7.1.3](#v713) - [Patch Changes](#patch-changes-23) - - [v7.1.1](#v711) + - [v7.1.2](#v712) - [Patch Changes](#patch-changes-24) + - [v7.1.1](#v711) + - [Patch Changes](#patch-changes-25) - [v7.1.0](#v710) - [Minor Changes](#minor-changes-8) - - [Patch Changes](#patch-changes-25) + - [Patch Changes](#patch-changes-26) - [Changes by Package](#changes-by-package-7) - [v7.0.2](#v702) - - [Patch Changes](#patch-changes-26) - - [v7.0.1](#v701) - [Patch Changes](#patch-changes-27) + - [v7.0.1](#v701) + - [Patch Changes](#patch-changes-28) - [v7.0.0](#v700) - [Breaking Changes](#breaking-changes) - [Package Restructuring](#package-restructuring) @@ -143,201 +145,201 @@ We manage release notes in this file instead of the paginated Github Releases Pa - [Major Changes (`react-router`)](#major-changes-react-router) - [Major Changes (`@react-router/*`)](#major-changes-react-router-1) - [Minor Changes](#minor-changes-9) - - [Patch Changes](#patch-changes-28) + - [Patch Changes](#patch-changes-29) - [Changes by Package](#changes-by-package-8) - [React Router v6 Releases](#react-router-v6-releases) - [v6.30.1](#v6301) - - [Patch Changes](#patch-changes-29) + - [Patch Changes](#patch-changes-30) - [v6.30.0](#v6300) - [Minor Changes](#minor-changes-10) - - [Patch Changes](#patch-changes-30) + - [Patch Changes](#patch-changes-31) - [v6.29.0](#v6290) - [Minor Changes](#minor-changes-11) - - [Patch Changes](#patch-changes-31) - - [v6.28.2](#v6282) - [Patch Changes](#patch-changes-32) - - [v6.28.1](#v6281) + - [v6.28.2](#v6282) - [Patch Changes](#patch-changes-33) + - [v6.28.1](#v6281) + - [Patch Changes](#patch-changes-34) - [v6.28.0](#v6280) - [What's Changed](#whats-changed-7) - [Minor Changes](#minor-changes-12) - - [Patch Changes](#patch-changes-34) + - [Patch Changes](#patch-changes-35) - [v6.27.0](#v6270) - [What's Changed](#whats-changed-8) - [Stabilized APIs](#stabilized-apis) - [Minor Changes](#minor-changes-13) - - [Patch Changes](#patch-changes-35) - - [v6.26.2](#v6262) - [Patch Changes](#patch-changes-36) - - [v6.26.1](#v6261) + - [v6.26.2](#v6262) - [Patch Changes](#patch-changes-37) + - [v6.26.1](#v6261) + - [Patch Changes](#patch-changes-38) - [v6.26.0](#v6260) - [Minor Changes](#minor-changes-14) - - [Patch Changes](#patch-changes-38) - - [v6.25.1](#v6251) - [Patch Changes](#patch-changes-39) + - [v6.25.1](#v6251) + - [Patch Changes](#patch-changes-40) - [v6.25.0](#v6250) - [What's Changed](#whats-changed-9) - [Stabilized `v7_skipActionErrorRevalidation`](#stabilized-v7_skipactionerrorrevalidation) - [Minor Changes](#minor-changes-15) - - [Patch Changes](#patch-changes-40) - - [v6.24.1](#v6241) - [Patch Changes](#patch-changes-41) + - [v6.24.1](#v6241) + - [Patch Changes](#patch-changes-42) - [v6.24.0](#v6240) - [What's Changed](#whats-changed-10) - [Lazy Route Discovery (a.k.a. "Fog of War")](#lazy-route-discovery-aka-fog-of-war) - [Minor Changes](#minor-changes-16) - - [Patch Changes](#patch-changes-42) - - [v6.23.1](#v6231) - [Patch Changes](#patch-changes-43) + - [v6.23.1](#v6231) + - [Patch Changes](#patch-changes-44) - [v6.23.0](#v6230) - [What's Changed](#whats-changed-11) - [Data Strategy (unstable)](#data-strategy-unstable) - [Skip Action Error Revalidation (unstable)](#skip-action-error-revalidation-unstable) - [Minor Changes](#minor-changes-17) - [v6.22.3](#v6223) - - [Patch Changes](#patch-changes-44) - - [v6.22.2](#v6222) - [Patch Changes](#patch-changes-45) - - [v6.22.1](#v6221) + - [v6.22.2](#v6222) - [Patch Changes](#patch-changes-46) + - [v6.22.1](#v6221) + - [Patch Changes](#patch-changes-47) - [v6.22.0](#v6220) - [What's Changed](#whats-changed-12) - [Core Web Vitals Technology Report Flag](#core-web-vitals-technology-report-flag) - [Minor Changes](#minor-changes-18) - - [Patch Changes](#patch-changes-47) - - [v6.21.3](#v6213) - [Patch Changes](#patch-changes-48) - - [v6.21.2](#v6212) + - [v6.21.3](#v6213) - [Patch Changes](#patch-changes-49) - - [v6.21.1](#v6211) + - [v6.21.2](#v6212) - [Patch Changes](#patch-changes-50) + - [v6.21.1](#v6211) + - [Patch Changes](#patch-changes-51) - [v6.21.0](#v6210) - [What's Changed](#whats-changed-13) - [`future.v7_relativeSplatPath`](#futurev7_relativesplatpath) - [Partial Hydration](#partial-hydration) - [Minor Changes](#minor-changes-19) - - [Patch Changes](#patch-changes-51) - - [v6.20.1](#v6201) - [Patch Changes](#patch-changes-52) + - [v6.20.1](#v6201) + - [Patch Changes](#patch-changes-53) - [v6.20.0](#v6200) - [Minor Changes](#minor-changes-20) - - [Patch Changes](#patch-changes-53) + - [Patch Changes](#patch-changes-54) - [v6.19.0](#v6190) - [What's Changed](#whats-changed-14) - [`unstable_flushSync` API](#unstable_flushsync-api) - [Minor Changes](#minor-changes-21) - - [Patch Changes](#patch-changes-54) + - [Patch Changes](#patch-changes-55) - [v6.18.0](#v6180) - [What's Changed](#whats-changed-15) - [New Fetcher APIs](#new-fetcher-apis) - [Persistence Future Flag (`future.v7_fetcherPersist`)](#persistence-future-flag-futurev7_fetcherpersist) - [Minor Changes](#minor-changes-22) - - [Patch Changes](#patch-changes-55) + - [Patch Changes](#patch-changes-56) - [v6.17.0](#v6170) - [What's Changed](#whats-changed-16) - [View Transitions 🚀](#view-transitions-) - [Minor Changes](#minor-changes-23) - - [Patch Changes](#patch-changes-56) + - [Patch Changes](#patch-changes-57) - [v6.16.0](#v6160) - [Minor Changes](#minor-changes-24) - - [Patch Changes](#patch-changes-57) + - [Patch Changes](#patch-changes-58) - [v6.15.0](#v6150) - [Minor Changes](#minor-changes-25) - - [Patch Changes](#patch-changes-58) - - [v6.14.2](#v6142) - [Patch Changes](#patch-changes-59) - - [v6.14.1](#v6141) + - [v6.14.2](#v6142) - [Patch Changes](#patch-changes-60) + - [v6.14.1](#v6141) + - [Patch Changes](#patch-changes-61) - [v6.14.0](#v6140) - [What's Changed](#whats-changed-17) - [JSON/Text Submissions](#jsontext-submissions) - [Minor Changes](#minor-changes-26) - - [Patch Changes](#patch-changes-61) + - [Patch Changes](#patch-changes-62) - [v6.13.0](#v6130) - [What's Changed](#whats-changed-18) - [`future.v7_startTransition`](#futurev7_starttransition) - [Minor Changes](#minor-changes-27) - - [Patch Changes](#patch-changes-62) - - [v6.12.1](#v6121) - [Patch Changes](#patch-changes-63) + - [v6.12.1](#v6121) + - [Patch Changes](#patch-changes-64) - [v6.12.0](#v6120) - [What's Changed](#whats-changed-19) - [`React.startTransition` support](#reactstarttransition-support) - [Minor Changes](#minor-changes-28) - - [Patch Changes](#patch-changes-64) - - [v6.11.2](#v6112) - [Patch Changes](#patch-changes-65) - - [v6.11.1](#v6111) + - [v6.11.2](#v6112) - [Patch Changes](#patch-changes-66) + - [v6.11.1](#v6111) + - [Patch Changes](#patch-changes-67) - [v6.11.0](#v6110) - [Minor Changes](#minor-changes-29) - - [Patch Changes](#patch-changes-67) + - [Patch Changes](#patch-changes-68) - [v6.10.0](#v6100) - [What's Changed](#whats-changed-20) - [Minor Changes](#minor-changes-30) - [`future.v7_normalizeFormMethod`](#futurev7_normalizeformmethod) - - [Patch Changes](#patch-changes-68) + - [Patch Changes](#patch-changes-69) - [v6.9.0](#v690) - [What's Changed](#whats-changed-21) - [`Component`/`ErrorBoundary` route properties](#componenterrorboundary-route-properties) - [Introducing Lazy Route Modules](#introducing-lazy-route-modules) - [Minor Changes](#minor-changes-31) - - [Patch Changes](#patch-changes-69) - - [v6.8.2](#v682) - [Patch Changes](#patch-changes-70) - - [v6.8.1](#v681) + - [v6.8.2](#v682) - [Patch Changes](#patch-changes-71) + - [v6.8.1](#v681) + - [Patch Changes](#patch-changes-72) - [v6.8.0](#v680) - [Minor Changes](#minor-changes-32) - - [Patch Changes](#patch-changes-72) + - [Patch Changes](#patch-changes-73) - [v6.7.0](#v670) - [Minor Changes](#minor-changes-33) - - [Patch Changes](#patch-changes-73) - - [v6.6.2](#v662) - [Patch Changes](#patch-changes-74) - - [v6.6.1](#v661) + - [v6.6.2](#v662) - [Patch Changes](#patch-changes-75) + - [v6.6.1](#v661) + - [Patch Changes](#patch-changes-76) - [v6.6.0](#v660) - [What's Changed](#whats-changed-22) - [Minor Changes](#minor-changes-34) - - [Patch Changes](#patch-changes-76) + - [Patch Changes](#patch-changes-77) - [v6.5.0](#v650) - [What's Changed](#whats-changed-23) - [Minor Changes](#minor-changes-35) - - [Patch Changes](#patch-changes-77) - - [v6.4.5](#v645) - [Patch Changes](#patch-changes-78) - - [v6.4.4](#v644) + - [v6.4.5](#v645) - [Patch Changes](#patch-changes-79) - - [v6.4.3](#v643) + - [v6.4.4](#v644) - [Patch Changes](#patch-changes-80) - - [v6.4.2](#v642) + - [v6.4.3](#v643) - [Patch Changes](#patch-changes-81) - - [v6.4.1](#v641) + - [v6.4.2](#v642) - [Patch Changes](#patch-changes-82) + - [v6.4.1](#v641) + - [Patch Changes](#patch-changes-83) - [v6.4.0](#v640) - [What's Changed](#whats-changed-24) - [Remix Data APIs](#remix-data-apis) - - [Patch Changes](#patch-changes-83) + - [Patch Changes](#patch-changes-84) - [v6.3.0](#v630) - [Minor Changes](#minor-changes-36) - [v6.2.2](#v622) - - [Patch Changes](#patch-changes-84) - - [v6.2.1](#v621) - [Patch Changes](#patch-changes-85) + - [v6.2.1](#v621) + - [Patch Changes](#patch-changes-86) - [v6.2.0](#v620) - [Minor Changes](#minor-changes-37) - - [Patch Changes](#patch-changes-86) - - [v6.1.1](#v611) - [Patch Changes](#patch-changes-87) + - [v6.1.1](#v611) + - [Patch Changes](#patch-changes-88) - [v6.1.0](#v610) - [Minor Changes](#minor-changes-38) - - [Patch Changes](#patch-changes-88) - - [v6.0.2](#v602) - [Patch Changes](#patch-changes-89) - - [v6.0.1](#v601) + - [v6.0.2](#v602) - [Patch Changes](#patch-changes-90) + - [v6.0.1](#v601) + - [Patch Changes](#patch-changes-91) - [v6.0.0](#v600) @@ -365,6 +367,19 @@ Date: YYYY-MM-DD **Full Changelog**: [`v7.X.Y...v7.X.Y`](https://github.com/remix-run/react-router/compare/react-router@7.X.Y...react-router@7.X.Y) --> +## v7.9.3 + +Date: 2025-09-26 + +### Patch Changes + +- `react-router` - Fix Data Mode regression causing a 404 during initial load in when `middleware` exists without any `loader` functions ([#14393](https://github.com/remix-run/react-router/pull/14393)) +- `react-router` - Do not try to use `turbo-stream` to decode CDN errors that never reached the server ([#14385](https://github.com/remix-run/react-router/pull/14385)) + - This was logic we used to have in Remix v2 that got lost in the adoption of Single Fetch + - This permits the actual CDN error to bubble to the `ErrorBoundary` instead of a generic _"Unable to decode turbo-stream response"_ error + +**Full Changelog**: [`v7.9.2...v7.9.3`](https://github.com/remix-run/react-router/compare/react-router@7.9.2...react-router@7.9.3) + ## v7.9.2 Date: 2025-09-24 diff --git a/docs/start/framework/deploying.md b/docs/start/framework/deploying.md index 1ed1dee57f..74e3165166 100644 --- a/docs/start/framework/deploying.md +++ b/docs/start/framework/deploying.md @@ -81,37 +81,12 @@ The containerized application can be deployed to any platform that supports Dock ### Vercel -``` -npx create-react-router@latest --template remix-run/react-router-templates/vercel -``` - -- Server Rendering -- Tailwind CSS - -### Cloudflare Workers w/ D1 - -``` -npx create-react-router@latest --template remix-run/react-router-templates/cloudflare-d1 -``` - -- Server Rendering -- D1 Database with Drizzle ORM -- Tailwind CSS +Vercel maintains their own template for React Router. Checkout the [Vercel Guide](https://vercel.com/templates/react-router/react-router-boilerplate) for more information. ### Cloudflare Workers -``` -npx create-react-router@latest --template remix-run/react-router-templates/cloudflare -``` - -- Server Rendering -- Tailwind CSS +Cloudflare maintains their own template for React Router. Checkout the [Cloudflare Guide](https://developers.cloudflare.com/workers/framework-guides/web-apps/react-router/) for more information. ### Netlify -``` -npx create-react-router@latest --template remix-run/react-router-templates/netlify -``` - -- Server Rendering -- Tailwind CSS +Netlify maintains their own template for React Router. Checkout the [Netlify Guide](https://docs.netlify.com/build/frameworks/framework-setup-guides/react-router/) for more information. diff --git a/integration/error-boundary-v2-test.ts b/integration/error-boundary-v2-test.ts index 32602e10a6..87d0fcbdfc 100644 --- a/integration/error-boundary-v2-test.ts +++ b/integration/error-boundary-v2-test.ts @@ -177,8 +177,8 @@ test.describe("ErrorBoundary", () => { await waitForAndAssert( page, app, - "#parent-error", - "Unable to decode turbo-stream response", + "#parent-error-response", + "500 CDN Error!", ); }); }); diff --git a/integration/error-data-request-test.ts b/integration/error-data-request-test.ts index 772ef8af57..187ac67273 100644 --- a/integration/error-data-request-test.ts +++ b/integration/error-data-request-test.ts @@ -110,7 +110,7 @@ test.describe("ErrorBoundary", () => { let { status, headers, data } = await fixture.requestSingleFetchData("/_root.data"); expect(status).toBe(200); - expect(headers.has("X-Remix-Error")).toBe(false); + expect(headers.has("X-Remix-Response")).toBe(true); expect(data).toEqual({}); }); @@ -122,7 +122,7 @@ test.describe("ErrorBoundary", () => { }, ); expect(status).toBe(405); - expect(headers.has("X-Remix-Error")).toBe(false); + expect(headers.has("X-Remix-Response")).toBe(true); expect(data).toEqual({ error: new ErrorResponseImpl( 405, @@ -153,7 +153,7 @@ test.describe("ErrorBoundary", () => { "/i/match/nothing.data", ); expect(status).toBe(404); - expect(headers.has("X-Remix-Error")).toBe(false); + expect(headers.has("X-Remix-Response")).toBe(true); expect(data).toEqual({ root: { error: new ErrorResponseImpl( diff --git a/packages/create-react-router/CHANGELOG.md b/packages/create-react-router/CHANGELOG.md index 46248d8c92..da45cd74b2 100644 --- a/packages/create-react-router/CHANGELOG.md +++ b/packages/create-react-router/CHANGELOG.md @@ -1,5 +1,7 @@ # `create-react-router` +## 7.9.3 + ## 7.9.2 _No changes_ diff --git a/packages/create-react-router/package.json b/packages/create-react-router/package.json index a5ad8e609f..f0eab96b1a 100644 --- a/packages/create-react-router/package.json +++ b/packages/create-react-router/package.json @@ -1,6 +1,6 @@ { "name": "create-react-router", - "version": "7.9.2", + "version": "7.9.3", "description": "Create a new React Router app", "homepage": "https://reactrouter.com", "bugs": { diff --git a/packages/react-router-architect/CHANGELOG.md b/packages/react-router-architect/CHANGELOG.md index e014477037..5ec74a1162 100644 --- a/packages/react-router-architect/CHANGELOG.md +++ b/packages/react-router-architect/CHANGELOG.md @@ -1,5 +1,13 @@ # `@react-router/architect` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `react-router@7.9.3` + - `@react-router/node@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-architect/package.json b/packages/react-router-architect/package.json index 9e7c2c1110..9b63583c6f 100644 --- a/packages/react-router-architect/package.json +++ b/packages/react-router-architect/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/architect", - "version": "7.9.2", + "version": "7.9.3", "description": "Architect server request handler for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-cloudflare/CHANGELOG.md b/packages/react-router-cloudflare/CHANGELOG.md index f17a920747..d74b9aa8d9 100644 --- a/packages/react-router-cloudflare/CHANGELOG.md +++ b/packages/react-router-cloudflare/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/cloudflare` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `react-router@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-cloudflare/package.json b/packages/react-router-cloudflare/package.json index 9897f57dd9..249f43f0f8 100644 --- a/packages/react-router-cloudflare/package.json +++ b/packages/react-router-cloudflare/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/cloudflare", - "version": "7.9.2", + "version": "7.9.3", "description": "Cloudflare platform abstractions for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-dev/CHANGELOG.md b/packages/react-router-dev/CHANGELOG.md index 260463e646..2d0aa8e6df 100644 --- a/packages/react-router-dev/CHANGELOG.md +++ b/packages/react-router-dev/CHANGELOG.md @@ -1,5 +1,14 @@ # `@react-router/dev` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `react-router@7.9.3` + - `@react-router/node@7.9.3` + - `@react-router/serve@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-dev/package.json b/packages/react-router-dev/package.json index 114f9c9f36..5d8c1a0e17 100644 --- a/packages/react-router-dev/package.json +++ b/packages/react-router-dev/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/dev", - "version": "7.9.2", + "version": "7.9.3", "description": "Dev tools and CLI for React Router", "homepage": "https://reactrouter.com", "bugs": { diff --git a/packages/react-router-dom/CHANGELOG.md b/packages/react-router-dom/CHANGELOG.md index eb67840d0a..75c2ecb0ef 100644 --- a/packages/react-router-dom/CHANGELOG.md +++ b/packages/react-router-dom/CHANGELOG.md @@ -1,5 +1,12 @@ # react-router-dom +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `react-router@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-dom/package.json b/packages/react-router-dom/package.json index a2061889b7..715770624e 100644 --- a/packages/react-router-dom/package.json +++ b/packages/react-router-dom/package.json @@ -1,6 +1,6 @@ { "name": "react-router-dom", - "version": "7.9.2", + "version": "7.9.3", "description": "Declarative routing for React web applications", "keywords": [ "react", diff --git a/packages/react-router-express/CHANGELOG.md b/packages/react-router-express/CHANGELOG.md index e58600466b..d3e49f9170 100644 --- a/packages/react-router-express/CHANGELOG.md +++ b/packages/react-router-express/CHANGELOG.md @@ -1,5 +1,13 @@ # `@react-router/express` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `react-router@7.9.3` + - `@react-router/node@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-express/package.json b/packages/react-router-express/package.json index 3ffab6f810..18f5bf6a13 100644 --- a/packages/react-router-express/package.json +++ b/packages/react-router-express/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/express", - "version": "7.9.2", + "version": "7.9.3", "description": "Express server request handler for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-fs-routes/CHANGELOG.md b/packages/react-router-fs-routes/CHANGELOG.md index 0336b8a6d8..0153883a9c 100644 --- a/packages/react-router-fs-routes/CHANGELOG.md +++ b/packages/react-router-fs-routes/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/fs-routes` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `@react-router/dev@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-fs-routes/package.json b/packages/react-router-fs-routes/package.json index 6a74cf9feb..d7728fd930 100644 --- a/packages/react-router-fs-routes/package.json +++ b/packages/react-router-fs-routes/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/fs-routes", - "version": "7.9.2", + "version": "7.9.3", "description": "File system routing conventions for React Router, for use within routes.ts", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-node/CHANGELOG.md b/packages/react-router-node/CHANGELOG.md index c1e038fb65..8955ab93d2 100644 --- a/packages/react-router-node/CHANGELOG.md +++ b/packages/react-router-node/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/node` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `react-router@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-node/package.json b/packages/react-router-node/package.json index 296b51be4f..be542771c3 100644 --- a/packages/react-router-node/package.json +++ b/packages/react-router-node/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/node", - "version": "7.9.2", + "version": "7.9.3", "description": "Node.js platform abstractions for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-remix-routes-option-adapter/CHANGELOG.md b/packages/react-router-remix-routes-option-adapter/CHANGELOG.md index 8ec81f0cf2..7d95d12953 100644 --- a/packages/react-router-remix-routes-option-adapter/CHANGELOG.md +++ b/packages/react-router-remix-routes-option-adapter/CHANGELOG.md @@ -1,5 +1,12 @@ # `@react-router/remix-config-routes-adapter` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `@react-router/dev@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-remix-routes-option-adapter/package.json b/packages/react-router-remix-routes-option-adapter/package.json index c38f0963a9..691b5353ab 100644 --- a/packages/react-router-remix-routes-option-adapter/package.json +++ b/packages/react-router-remix-routes-option-adapter/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/remix-routes-option-adapter", - "version": "7.9.2", + "version": "7.9.3", "description": "Adapter for Remix's \"routes\" config option, for use within routes.ts", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router-serve/CHANGELOG.md b/packages/react-router-serve/CHANGELOG.md index b918937616..cb2c8edc65 100644 --- a/packages/react-router-serve/CHANGELOG.md +++ b/packages/react-router-serve/CHANGELOG.md @@ -1,5 +1,14 @@ # `@react-router/serve` +## 7.9.3 + +### Patch Changes + +- Updated dependencies: + - `react-router@7.9.3` + - `@react-router/node@7.9.3` + - `@react-router/express@7.9.3` + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router-serve/package.json b/packages/react-router-serve/package.json index 42c995a848..4d42b286a5 100644 --- a/packages/react-router-serve/package.json +++ b/packages/react-router-serve/package.json @@ -1,6 +1,6 @@ { "name": "@react-router/serve", - "version": "7.9.2", + "version": "7.9.3", "description": "Production application server for React Router", "bugs": { "url": "https://github.com/remix-run/react-router/issues" diff --git a/packages/react-router/CHANGELOG.md b/packages/react-router/CHANGELOG.md index e164dc1201..2f0d19ed90 100644 --- a/packages/react-router/CHANGELOG.md +++ b/packages/react-router/CHANGELOG.md @@ -1,5 +1,14 @@ # `react-router` +## 7.9.3 + +### Patch Changes + +- Do not try to use `turbo-stream` to decode CDN errors that never reached the server ([#14385](https://github.com/remix-run/react-router/pull/14385)) + - We used to do this but lost this check with the adoption of single fetch + +- Fix Data Mode regression causing a 404 during initial load in when `middleware` exists without any `loader` functions ([#14393](https://github.com/remix-run/react-router/pull/14393)) + ## 7.9.2 ### Patch Changes diff --git a/packages/react-router/__tests__/router/context-middleware-test.tsx b/packages/react-router/__tests__/router/context-middleware-test.tsx index 9db190d373..cea82e235c 100644 --- a/packages/react-router/__tests__/router/context-middleware-test.tsx +++ b/packages/react-router/__tests__/router/context-middleware-test.tsx @@ -300,6 +300,62 @@ describe("context/middleware", () => { ]); }); + it("runs middleware on initialization even if no loaders exist", async () => { + let snapshot; + router = createRouter({ + history: createMemoryHistory(), + routes: [ + { + path: "/", + middleware: [ + async ({ context }, next) => { + await next(); + // Grab a snapshot at the end of the upwards middleware chain + snapshot = context.get(orderContext); + }, + getOrderMiddleware(orderContext, "a"), + getOrderMiddleware(orderContext, "b"), + ], + children: [ + { + index: true, + middleware: [ + getOrderMiddleware(orderContext, "c"), + getOrderMiddleware(orderContext, "d"), + ], + }, + ], + }, + ], + }); + let initPromise = new Promise((r) => { + let unsub = router.subscribe((state) => { + if (state.initialized) { + unsub(); + r(undefined); + } + }); + }); + await router.initialize(); + await initPromise; + expect(router.state).toMatchObject({ + initialized: true, + location: { pathname: "/" }, + navigation: { state: "idle" }, + errors: null, + }); + expect(snapshot).toEqual([ + "a middleware - before next()", + "b middleware - before next()", + "c middleware - before next()", + "d middleware - before next()", + "d middleware - after next()", + "c middleware - after next()", + "b middleware - after next()", + "a middleware - after next()", + ]); + }); + it("runs middleware even if no loaders exist", async () => { let snapshot; router = createRouter({ diff --git a/packages/react-router/__tests__/server-runtime/server-test.ts b/packages/react-router/__tests__/server-runtime/server-test.ts index 69ab365b3a..725c49792e 100644 --- a/packages/react-router/__tests__/server-runtime/server-test.ts +++ b/packages/react-router/__tests__/server-runtime/server-test.ts @@ -555,7 +555,7 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(400); - expect(result.headers.get("X-Remix-Error")).toBe("yes"); + expect(headers.has("X-Remix-Response")).toBe(true); expect((await result.json()).message).toBeTruthy(); }); @@ -585,7 +585,7 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(403); - expect(result.headers.get("X-Remix-Error")).toBe("yes"); + expect(headers.has("X-Remix-Response")).toBe(true); expect((await result.json()).message).toBeTruthy(); }); @@ -608,7 +608,7 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(404); - expect(result.headers.get("X-Remix-Error")).toBe("yes"); + expect(headers.has("X-Remix-Response")).toBe(true); expect((await result.json()).message).toBeTruthy(); }); @@ -670,7 +670,7 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(500); expect((await result.json()).message).toBe("Unexpected Server Error"); - expect(result.headers.get("X-Remix-Error")).toBe("yes"); + expect(headers.has("X-Remix-Response")).toBe(true); expect(rootLoader.mock.calls.length).toBe(1); expect(testAction.mock.calls.length).toBe(0); }); @@ -704,7 +704,7 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(500); expect((await result.json()).message).toBe(message); - expect(result.headers.get("X-Remix-Error")).toBe("yes"); + expect(headers.has("X-Remix-Response")).toBe(true); expect(rootLoader.mock.calls.length).toBe(1); expect(testAction.mock.calls.length).toBe(0); expect(spy.console.mock.calls.length).toBe(1); @@ -737,7 +737,6 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(400); expect(await result.text()).toBe("test"); - expect(result.headers.get("X-Remix-Catch")).toBe("yes"); expect(rootLoader.mock.calls.length).toBe(1); expect(testAction.mock.calls.length).toBe(0); }); @@ -800,7 +799,7 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(500); expect((await result.json()).message).toBe("Unexpected Server Error"); - expect(result.headers.get("X-Remix-Error")).toBe("yes"); + expect(headers.has("X-Remix-Response")).toBe(true); expect(rootLoader.mock.calls.length).toBe(0); expect(testAction.mock.calls.length).toBe(1); }); @@ -834,7 +833,7 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(500); expect((await result.json()).message).toBe(message); - expect(result.headers.get("X-Remix-Error")).toBe("yes"); + expect(headers.has("X-Remix-Response")).toBe(true); expect(rootLoader.mock.calls.length).toBe(0); expect(testAction.mock.calls.length).toBe(1); expect(spy.console.mock.calls.length).toBe(1); @@ -867,7 +866,6 @@ describe("shared server runtime", () => { let result = await handler(request); expect(result.status).toBe(400); expect(await result.text()).toBe("test"); - expect(result.headers.get("X-Remix-Catch")).toBe("yes"); expect(rootLoader.mock.calls.length).toBe(0); expect(testAction.mock.calls.length).toBe(1); }); diff --git a/packages/react-router/lib/dom/ssr/single-fetch.tsx b/packages/react-router/lib/dom/ssr/single-fetch.tsx index 4043e35c46..bffc02708d 100644 --- a/packages/react-router/lib/dom/ssr/single-fetch.tsx +++ b/packages/react-router/lib/dom/ssr/single-fetch.tsx @@ -610,10 +610,14 @@ async function fetchAndDecodeViaTurboStream( let res = await fetch(url, await createRequestInit(request)); - // If this 404'd without hitting the running server (most likely in a - // pre-rendered app using a CDN), then bubble a standard 404 ErrorResponse - if (res.status === 404 && !res.headers.has("X-Remix-Response")) { - throw new ErrorResponseImpl(404, "Not Found", true); + // If this error'd without hitting the running server, then bubble a normal + // `ErrorResponse` and don't try to decode the body with `turbo-stream`. + // + // This could be triggered by a few scenarios: + // - `.data` request 404 on a pre-rendered app using a CDN + // - 429 error returned from a CDN on a SSR app + if (res.status >= 400 && !res.headers.has("X-Remix-Response")) { + throw new ErrorResponseImpl(res.status, res.statusText, await res.text()); } // Handle non-RR redirects (i.e., from express middleware) diff --git a/packages/react-router/lib/router/router.ts b/packages/react-router/lib/router/router.ts index 2c3e379345..4cd9d1df51 100644 --- a/packages/react-router/lib/router/router.ts +++ b/packages/react-router/lib/router/router.ts @@ -5744,13 +5744,22 @@ function getDataStrategyMatch( return shouldRevalidateLoader(match, unstable_shouldRevalidateArgs); }, resolve(handlerOverride) { - if ( + let { lazy, loader, middleware } = match.route; + + let callHandler = isUsingNewApi || shouldLoad || (handlerOverride && !isMutationMethod(request.method) && - (match.route.lazy || match.route.loader)) - ) { + (lazy || loader)); + + // If this match was marked `shouldLoad` due to a middleware and it + // doesn't have a `loader` to run and no `lazy` to add one, then we can + // just return undefined from the "loader" here + let isMiddlewareOnlyRoute = + middleware && middleware.length > 0 && !loader && !lazy; + + if (callHandler && !isMiddlewareOnlyRoute) { return callLoaderOrAction({ request, match, diff --git a/packages/react-router/lib/rsc/browser.tsx b/packages/react-router/lib/rsc/browser.tsx index 762773c639..9b9974bc70 100644 --- a/packages/react-router/lib/rsc/browser.tsx +++ b/packages/react-router/lib/rsc/browser.tsx @@ -530,10 +530,14 @@ function getFetchAndDecodeViaRSC( new Request(url, await createRequestInit(request)), ); - // If this 404'd without hitting the running server (most likely in a - // pre-rendered app using a CDN), then bubble a standard 404 ErrorResponse - if (res.status === 404 && !res.headers.has("X-Remix-Response")) { - throw new ErrorResponseImpl(404, "Not Found", true); + // If this error'd without hitting the running server, then bubble a normal + // `ErrorResponse` and don't try to decode the body with `turbo-stream`. + // + // This could be triggered by a few scenarios: + // - `.data` request 404 on a pre-rendered app using a CDN + // - 429 error returned from a CDN on a SSR app + if (res.status >= 400 && !res.headers.has("X-Remix-Response")) { + throw new ErrorResponseImpl(res.status, res.statusText, await res.text()); } invariant(res.body, "No response body to decode"); diff --git a/packages/react-router/lib/rsc/server.rsc.ts b/packages/react-router/lib/rsc/server.rsc.ts index d8880506ac..c911c7641a 100644 --- a/packages/react-router/lib/rsc/server.rsc.ts +++ b/packages/react-router/lib/rsc/server.rsc.ts @@ -455,8 +455,8 @@ export async function matchRSCServerRequest({ generateResponse, temporaryReferences, ); - // The front end uses this to know whether a 404 status came from app code - // or 404'd and never reached the origin server + // The front end uses this to know whether a 4xx/5xx status came from app code + // or never reached the origin server response.headers.set("X-Remix-Response", "yes"); return response; } diff --git a/packages/react-router/lib/rsc/server.ssr.tsx b/packages/react-router/lib/rsc/server.ssr.tsx index 7eeb8a91f6..7662d29947 100644 --- a/packages/react-router/lib/rsc/server.ssr.tsx +++ b/packages/react-router/lib/rsc/server.ssr.tsx @@ -196,7 +196,7 @@ export async function routeRSCServerRequest({ headers.delete("Content-Encoding"); headers.delete("Content-Length"); headers.delete("Content-Type"); - headers.delete("x-remix-response"); + headers.delete("X-Remix-Response"); headers.set("Location", payload.location); return new Response(serverResponseB?.body || "", { diff --git a/packages/react-router/lib/server-runtime/server.ts b/packages/react-router/lib/server-runtime/server.ts index d3b78ae510..71f4d11b04 100644 --- a/packages/react-router/lib/server-runtime/server.ts +++ b/packages/react-router/lib/server-runtime/server.ts @@ -661,9 +661,6 @@ async function handleResourceRequest( function handleQueryRouteError(error: unknown) { if (isResponse(error)) { - // Note: Not functionally required but ensures that our response headers - // match identically to what Remix returns - error.headers.set("X-Remix-Catch", "yes"); return error; } @@ -701,9 +698,6 @@ function errorResponseToJson( { status: errorResponse.status, statusText: errorResponse.statusText, - headers: { - "X-Remix-Error": "yes", - }, }, ); } diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 59376903f1..563bcad27c 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -1,6 +1,6 @@ { "name": "react-router", - "version": "7.9.2", + "version": "7.9.3", "description": "Declarative routing for React", "keywords": [ "react",