- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 1.3k
Description
Which project does this relate to?
Start
Describe the bug
Hot Module Reloading (HMR) does not re-run beforeLoad or loader functions when their code changes.
Even though Vite logs indicate a hot update ([vite] program reload), the functions retain old behavior until a full page refresh.
This causes issues when working on things like localization or dynamic imports that rely on beforeLoad.
Your Example Website or App
create-tanstack base project
Steps to Reproduce the Bug or Issue
- Create a new project:
   pnpm create @tanstack/start@latest- Add this to src/routes/index.tsx:
export const Route = createFileRoute("/")({
  component: App,
  beforeLoad: async () => {
    console.log("hi");
  },
});- Run the dev server and edit the console.log message.
Observed behavior:
•	The console still prints the old message until a full refresh.
•	The Vite terminal logs show:
[vite] (client) hmr update /src/routes/index.tsx
[vite] (ssr) page reload src/routes/index.tsx
[vite] program reload
•	Using the same code in a useEffect does hot reload properly.
Adding this snippet at the bottom of each route fixes the problem:
if (import.meta.hot) {
  import.meta.hot.accept(() => {
    import.meta.hot.invalidate();
  });
}…but it’s not practical to add to every route file manually.
Expected behavior
HMR should automatically re-run updated beforeLoad and loader code without requiring a full page refresh.
Additional context
💡 Suggested Fix
A simple Vite plugin resolves the issue by invalidating router modules when route files change:
import type { Plugin } from "vite"
export function tanstackRouterHMR(): Plugin {
  return {
    name: "tanstack-router-hmr",
    enforce: "post",
    handleHotUpdate(ctx) {
      const invalidatedModules = []
      for (const mod of ctx.server.moduleGraph.idToModuleMap.values()) {
        if (mod.id?.includes("/router.ts")) {
          invalidatedModules.push(mod)
        }
      }
      return invalidatedModules
    },
  }
}This ensures route changes propagate correctly through routeTree.gen.ts and router.tsx.
⸻
🧩 Context
Discovered while editing .json message files for i18next — changes to localized messages didn’t reflect until a hard refresh, even though they were dynamically imported inside beforeLoad.