diff --git a/.changeset/fluffy-peas-promise.md b/.changeset/fluffy-peas-promise.md
new file mode 100644
index 00000000000..b873d666ab0
--- /dev/null
+++ b/.changeset/fluffy-peas-promise.md
@@ -0,0 +1,5 @@
+---
+"@remix-run/server-runtime": patch
+---
+
+Add a more specific error if a user returns a `defer` response from a resource route
diff --git a/integration/resource-routes-test.ts b/integration/resource-routes-test.ts
index 11639ffaaed..def0f01e15b 100644
--- a/integration/resource-routes-test.ts
+++ b/integration/resource-routes-test.ts
@@ -57,6 +57,11 @@ test.describe("loader in an app", async () => {
"app/routes/redirect-destination.tsx": js`
export default () =>
You made it!
`,
+ "app/routes/defer.tsx": js`
+ import { defer } from "@remix-run/node";
+
+ export let loader = () => defer({ data: 'whatever' });
+ `,
"app/routes/data[.]json.tsx": js`
import { json } from "@remix-run/node";
export let loader = () => json({hello: "world"});
@@ -252,6 +257,18 @@ test.describe("loader in an app", async () => {
'Route "routes/no-action" does not have an action'
);
});
+
+ test("should error if a defer is returned from a resource route", async ({
+ page,
+ }) => {
+ let app = new PlaywrightFixture(appFixture, page);
+ let res = await app.goto("/defer");
+ expect(res.status()).toBe(500);
+ expect(await res.text()).toMatch(
+ "You cannot return a `defer()` response from a Resource Route. " +
+ 'Did you forget to export a default UI component from the "routes/defer" route?'
+ );
+ });
});
test.describe("Development server", async () => {
diff --git a/packages/remix-server-runtime/server.ts b/packages/remix-server-runtime/server.ts
index e92933f74b2..ab5683ed784 100644
--- a/packages/remix-server-runtime/server.ts
+++ b/packages/remix-server-runtime/server.ts
@@ -425,6 +425,11 @@ async function handleResourceRequestRR(
routeId,
requestContext: loadContext,
});
+ invariant(
+ !(DEFERRED_SYMBOL in response),
+ `You cannot return a \`defer()\` response from a Resource Route. Did you ` +
+ `forget to export a default UI component from the "${routeId}" route?`
+ );
// callRouteLoader/callRouteAction always return responses
invariant(
isResponse(response),