diff --git a/.changeset/purple-poems-laugh.md b/.changeset/purple-poems-laugh.md new file mode 100644 index 0000000000..b4501690e9 --- /dev/null +++ b/.changeset/purple-poems-laugh.md @@ -0,0 +1,5 @@ +--- +"react-router": patch +--- + +Fix redirects returned from loaders/actions using `data()` diff --git a/integration/defer-loader-test.ts b/integration/defer-loader-test.ts index 534716b389..9fc9beb6f6 100644 --- a/integration/defer-loader-test.ts +++ b/integration/defer-loader-test.ts @@ -40,7 +40,9 @@ test.describe("deferred loaders", () => { } ); } - export default function Redirect() {return null;} + export default function Redirect() { + return null; + } `, "app/routes/direct-promise-access.tsx": js` @@ -82,7 +84,7 @@ test.describe("deferred loaders", () => { test.afterAll(async () => appFixture.close()); - test.skip("deferred response can redirect on document request", async ({ + test("deferred response can redirect on document request", async ({ page, }) => { let app = new PlaywrightFixture(appFixture, page); @@ -90,9 +92,7 @@ test.describe("deferred loaders", () => { await page.waitForURL(/\?redirected/); }); - test.skip("deferred response can redirect on transition", async ({ - page, - }) => { + test("deferred response can redirect on transition", async ({ page }) => { let app = new PlaywrightFixture(appFixture, page); await app.goto("/"); await app.clickLink("/redirect"); diff --git a/packages/react-router/lib/server-runtime/data.ts b/packages/react-router/lib/server-runtime/data.ts index 0ba1385808..8249c22946 100644 --- a/packages/react-router/lib/server-runtime/data.ts +++ b/packages/react-router/lib/server-runtime/data.ts @@ -1,3 +1,5 @@ +import { isDataWithResponseInit } from "../router/router"; +import { isRedirectStatusCode } from "./responses"; import type { ActionFunction, ActionFunctionArgs, @@ -32,6 +34,16 @@ export async function callRouteHandler( context: args.context, }); + // If they returned a redirect via data(), re-throw it as a Response + if ( + isDataWithResponseInit(result) && + result.init && + result.init.status && + isRedirectStatusCode(result.init.status) + ) { + throw new Response(null, result.init); + } + return result; }