diff --git a/.changeset/empty-default-export.md b/.changeset/empty-default-export.md
new file mode 100644
index 00000000000..f19b394bb24
--- /dev/null
+++ b/.changeset/empty-default-export.md
@@ -0,0 +1,8 @@
+---
+"@remix-run/react": patch
+---
+
+Fix warning that could be logged when using route files with no `default` export
+ - It seems our compiler compiles these files to export an empty object as the `default` which we can then end up passing to `React.createElement`, triggering the console warning, but generally no UI issues
+ - By properly detecting these, we can correctly pass `Component: undefined` off to the React Router layer
+ - This is technically an potential issue in the compiler but it's an easy patch in the `@remix-run/react` layer and hopefully disappears in a Vite world
diff --git a/packages/remix-react/__tests__/components-test.tsx b/packages/remix-react/__tests__/components-test.tsx
index fa2d5523757..1d48cc703de 100644
--- a/packages/remix-react/__tests__/components-test.tsx
+++ b/packages/remix-react/__tests__/components-test.tsx
@@ -1,10 +1,13 @@
+import { createStaticHandler } from "@remix-run/router";
+import { act, fireEvent, render } from "@testing-library/react";
import * as React from "react";
-import { createMemoryRouter, RouterProvider } from "react-router-dom";
-import { fireEvent, render, act } from "@testing-library/react";
+import { createMemoryRouter, Outlet, RouterProvider } from "react-router-dom";
+import { RemixBrowser } from "../browser";
import type { LiveReload as ActualLiveReload } from "../components";
import { Link, NavLink, RemixContext } from "../components";
-
+import invariant from "../invariant";
+import { RemixServer } from "../server";
import "@testing-library/jest-dom/extend-expect";
// TODO: Every time we touch LiveReload (without changing the API) these tests
@@ -199,3 +202,126 @@ describe("", () => {
describe("", () => {
itPrefetchesPageLinks(NavLink);
});
+
+describe("", () => {
+ it("handles empty default export objects from the compiler", async () => {
+ let staticHandlerContext = await createStaticHandler([{ path: "/" }]).query(
+ new Request("http://localhost/")
+ );
+ invariant(
+ !(staticHandlerContext instanceof Response),
+ "Expected a context"
+ );
+
+ let context = {
+ manifest: {
+ routes: {
+ root: {
+ hasLoader: false,
+ hasAction: false,
+ hasErrorBoundary: false,
+ id: "root",
+ module: "root.js",
+ path: "/",
+ },
+ empty: {
+ hasLoader: false,
+ hasAction: false,
+ hasErrorBoundary: false,
+ id: "empty",
+ module: "empty.js",
+ index: true,
+ parentId: "root",
+ },
+ },
+ entry: { imports: [], module: "" },
+ url: "",
+ version: "",
+ },
+ routeModules: {
+ root: {
+ default: () => {
+ return (
+ <>
+
Root
+
+ >
+ );
+ },
+ },
+ empty: { default: {} },
+ },
+ staticHandlerContext,
+ future: {},
+ };
+
+ jest.spyOn(console, "warn").mockImplementation(() => {});
+ jest.spyOn(console, "error");
+
+ let { container } = render(
+
+ );
+
+ expect(console.warn).toHaveBeenCalledWith(
+ 'Matched leaf route at location "/" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.'
+ );
+ expect(console.error).not.toHaveBeenCalled();
+ expect(container.innerHTML).toMatch("