diff --git a/integration/vite-css-test.ts b/integration/vite-css-test.ts index 863025c5c4..e02a95aafd 100644 --- a/integration/vite-css-test.ts +++ b/integration/vite-css-test.ts @@ -551,6 +551,10 @@ async function hmrWorkflow({ file: "styles-bundled.css", selector: "#css-bundled", }, + { + file: "styles.module.css", + selector: "#css-modules", + }, { file: "styles-postcss-linked.css", selector: "#css-postcss-linked", @@ -564,10 +568,6 @@ async function hmrWorkflow({ ...(routeBase === "rsc-server-first-route" ? [] : ([ - { - file: "styles.module.css", - selector: "#css-modules", - }, { file: "styles-vanilla-local.css.ts", selector: "#css-vanilla-local", diff --git a/packages/react-router-dev/vite/rsc/plugin.ts b/packages/react-router-dev/vite/rsc/plugin.ts index bdb90486cc..24a953c2c0 100644 --- a/packages/react-router-dev/vite/rsc/plugin.ts +++ b/packages/react-router-dev/vite/rsc/plugin.ts @@ -280,9 +280,19 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] { async hotUpdate(this, { server, file, modules }) { if (this.environment.name !== "rsc") return; + const clientModules = + server.environments.client.moduleGraph.getModulesByFile(file); + + const vite = await import("vite"); const isServerOnlyChange = - (server.environments.client.moduleGraph.getModulesByFile(file) - ?.size ?? 0) === 0; + !clientModules || + clientModules.size === 0 || + // Handle CSS injected from server-first routes (with ?direct query + // string) since the client graph has a reference to the CSS + (vite.isCSSRequest(file) && + Array.from(clientModules).some((mod) => + mod.id?.includes("?direct"), + )); for (const mod of getModulesWithImporters(modules)) { if (!mod.file) continue;