From 7cb2f05abb0aecb85a269d71faf513908cd1cc31 Mon Sep 17 00:00:00 2001
From: Manuel Schiller <manuel.schiller@caligano.de>
Date: Sat, 6 Jun 2026 10:19:59 +0200
Subject: [PATCH 1/2] fix(router-plugin): preserve generated route state for
 aliased HMR imports

fixes #4303
---
 .changeset/fix-route-hmr-aliased-imports.md   |   5 +
 .../AliasedRouteImportChildPanel.tsx          |  23 ++
 .../AliasedRouteImportParentPanel.tsx         |  24 ++
 e2e/react-start/hmr/src/routeTree.gen.ts      |  54 ++++
 .../aliased-route-imports.$id.child.tsx       |   6 +
 .../src/routes/aliased-route-imports.$id.tsx  |   6 +
 e2e/react-start/hmr/tests/app.spec.ts         |  84 +++++-
 .../src/core/hmr/handle-route-update.ts       |  14 +
 .../src/core/hmr/vite-adapter.ts              |  19 +-
 packages/router-plugin/tests/add-hmr.test.ts  |  15 +
 .../snapshots/react/arrow-function@true.tsx   | 265 ++++++++++--------
 .../react/arrow-function@webpack-hot.tsx      |   9 +
 .../createRootRoute-inline-component@true.tsx | 265 ++++++++++--------
 .../snapshots/react/createRootRoute@true.tsx  | 265 ++++++++++--------
 ...ateRootRouteWithContext-type-args@true.tsx | 265 ++++++++++--------
 .../explicit-undefined-component@true.tsx     | 265 ++++++++++--------
 .../react/function-declaration@true.tsx       | 265 ++++++++++--------
 .../snapshots/react/multi-component@true.tsx  | 265 ++++++++++--------
 .../react/string-literal-keys@true.tsx        | 265 ++++++++++--------
 .../snapshots/solid/arrow-function@true.tsx   | 265 ++++++++++--------
 .../tests/handle-route-update.test.ts         |  27 ++
 21 files changed, 1576 insertions(+), 1095 deletions(-)
 create mode 100644 .changeset/fix-route-hmr-aliased-imports.md
 create mode 100644 e2e/react-start/hmr/src/components/AliasedRouteImportChildPanel.tsx
 create mode 100644 e2e/react-start/hmr/src/components/AliasedRouteImportParentPanel.tsx
 create mode 100644 e2e/react-start/hmr/src/routes/aliased-route-imports.$id.child.tsx
 create mode 100644 e2e/react-start/hmr/src/routes/aliased-route-imports.$id.tsx

diff --git a/.changeset/fix-route-hmr-aliased-imports.md b/.changeset/fix-route-hmr-aliased-imports.md
new file mode 100644
index 0000000000..aa72bcc3af
--- /dev/null
+++ b/.changeset/fix-route-hmr-aliased-imports.md
@@ -0,0 +1,5 @@
+---
+'@tanstack/router-plugin': patch
+---
+
+Fix route HMR so aliased route imports keep generated route properties after hot reloads.
diff --git a/e2e/react-start/hmr/src/components/AliasedRouteImportChildPanel.tsx b/e2e/react-start/hmr/src/components/AliasedRouteImportChildPanel.tsx
new file mode 100644
index 0000000000..3ec6f36e35
--- /dev/null
+++ b/e2e/react-start/hmr/src/components/AliasedRouteImportChildPanel.tsx
@@ -0,0 +1,23 @@
+import { Link } from '@tanstack/react-router'
+import { Route as parentRoute } from '../routes/aliased-route-imports.$id'
+import { Route } from '../routes/aliased-route-imports.$id.child'
+
+export function AliasedRouteImportChildPanel() {
+  const { id } = Route.useParams()
+
+  return (
+    <main className="hmr-card flex flex-col gap-4">
+      <p className="hmr-marker" data-testid="aliased-child-marker">
+        aliased-child-baseline
+      </p>
+      <Link
+        className="hmr-button w-fit"
+        data-testid="aliased-back-parent"
+        params={{ id }}
+        to={parentRoute.to}
+      >
+        Back to aliased parent {id}
+      </Link>
+    </main>
+  )
+}
diff --git a/e2e/react-start/hmr/src/components/AliasedRouteImportParentPanel.tsx b/e2e/react-start/hmr/src/components/AliasedRouteImportParentPanel.tsx
new file mode 100644
index 0000000000..80ca3a9084
--- /dev/null
+++ b/e2e/react-start/hmr/src/components/AliasedRouteImportParentPanel.tsx
@@ -0,0 +1,24 @@
+import { Link, Outlet } from '@tanstack/react-router'
+import { Route as childRoute } from '../routes/aliased-route-imports.$id.child'
+import { Route } from '../routes/aliased-route-imports.$id'
+
+export function AliasedRouteImportParentPanel() {
+  const { id } = Route.useParams()
+
+  return (
+    <main className="hmr-card flex flex-col gap-4">
+      <p className="hmr-marker" data-testid="aliased-parent-marker">
+        aliased-parent-baseline
+      </p>
+      <Link
+        className="hmr-button w-fit"
+        data-testid="aliased-show-child"
+        params={{ id }}
+        to={childRoute.to}
+      >
+        Show aliased child {id}
+      </Link>
+      <Outlet />
+    </main>
+  )
+}
diff --git a/e2e/react-start/hmr/src/routeTree.gen.ts b/e2e/react-start/hmr/src/routeTree.gen.ts
index d2baf5bef6..53c89f8eb5 100644
--- a/e2e/react-start/hmr/src/routeTree.gen.ts
+++ b/e2e/react-start/hmr/src/routeTree.gen.ts
@@ -20,6 +20,8 @@ import { Route as ComponentHmrInlineErrorSplitRouteImport } from './routes/compo
 import { Route as ComponentHmrRouteImport } from './routes/component-hmr'
 import { Route as ChildRouteImport } from './routes/child'
 import { Route as IndexRouteImport } from './routes/index'
+import { Route as AliasedRouteImportsIdRouteImport } from './routes/aliased-route-imports.$id'
+import { Route as AliasedRouteImportsIdChildRouteImport } from './routes/aliased-route-imports.$id.child'
 
 const ServerFnHmrRoute = ServerFnHmrRouteImport.update({
   id: '/server-fn-hmr',
@@ -80,6 +82,17 @@ const IndexRoute = IndexRouteImport.update({
   path: '/',
   getParentRoute: () => rootRouteImport,
 } as any)
+const AliasedRouteImportsIdRoute = AliasedRouteImportsIdRouteImport.update({
+  id: '/aliased-route-imports/$id',
+  path: '/aliased-route-imports/$id',
+  getParentRoute: () => rootRouteImport,
+} as any)
+const AliasedRouteImportsIdChildRoute =
+  AliasedRouteImportsIdChildRouteImport.update({
+    id: '/child',
+    path: '/child',
+    getParentRoute: () => AliasedRouteImportsIdRoute,
+  } as any)
 
 export interface FileRoutesByFullPath {
   '/': typeof IndexRoute
@@ -93,6 +106,8 @@ export interface FileRoutesByFullPath {
   '/component-hmr-named-split': typeof ComponentHmrNamedSplitRoute
   '/inputs': typeof InputsRoute
   '/server-fn-hmr': typeof ServerFnHmrRoute
+  '/aliased-route-imports/$id': typeof AliasedRouteImportsIdRouteWithChildren
+  '/aliased-route-imports/$id/child': typeof AliasedRouteImportsIdChildRoute
 }
 export interface FileRoutesByTo {
   '/': typeof IndexRoute
@@ -106,6 +121,8 @@ export interface FileRoutesByTo {
   '/component-hmr-named-split': typeof ComponentHmrNamedSplitRoute
   '/inputs': typeof InputsRoute
   '/server-fn-hmr': typeof ServerFnHmrRoute
+  '/aliased-route-imports/$id': typeof AliasedRouteImportsIdRouteWithChildren
+  '/aliased-route-imports/$id/child': typeof AliasedRouteImportsIdChildRoute
 }
 export interface FileRoutesById {
   __root__: typeof rootRouteImport
@@ -120,6 +137,8 @@ export interface FileRoutesById {
   '/component-hmr-named-split': typeof ComponentHmrNamedSplitRoute
   '/inputs': typeof InputsRoute
   '/server-fn-hmr': typeof ServerFnHmrRoute
+  '/aliased-route-imports/$id': typeof AliasedRouteImportsIdRouteWithChildren
+  '/aliased-route-imports/$id/child': typeof AliasedRouteImportsIdChildRoute
 }
 export interface FileRouteTypes {
   fileRoutesByFullPath: FileRoutesByFullPath
@@ -135,6 +154,8 @@ export interface FileRouteTypes {
     | '/component-hmr-named-split'
     | '/inputs'
     | '/server-fn-hmr'
+    | '/aliased-route-imports/$id'
+    | '/aliased-route-imports/$id/child'
   fileRoutesByTo: FileRoutesByTo
   to:
     | '/'
@@ -148,6 +169,8 @@ export interface FileRouteTypes {
     | '/component-hmr-named-split'
     | '/inputs'
     | '/server-fn-hmr'
+    | '/aliased-route-imports/$id'
+    | '/aliased-route-imports/$id/child'
   id:
     | '__root__'
     | '/'
@@ -161,6 +184,8 @@ export interface FileRouteTypes {
     | '/component-hmr-named-split'
     | '/inputs'
     | '/server-fn-hmr'
+    | '/aliased-route-imports/$id'
+    | '/aliased-route-imports/$id/child'
   fileRoutesById: FileRoutesById
 }
 export interface RootRouteChildren {
@@ -175,6 +200,7 @@ export interface RootRouteChildren {
   ComponentHmrNamedSplitRoute: typeof ComponentHmrNamedSplitRoute
   InputsRoute: typeof InputsRoute
   ServerFnHmrRoute: typeof ServerFnHmrRoute
+  AliasedRouteImportsIdRoute: typeof AliasedRouteImportsIdRouteWithChildren
 }
 
 declare module '@tanstack/react-router' {
@@ -256,9 +282,36 @@ declare module '@tanstack/react-router' {
       preLoaderRoute: typeof IndexRouteImport
       parentRoute: typeof rootRouteImport
     }
+    '/aliased-route-imports/$id': {
+      id: '/aliased-route-imports/$id'
+      path: '/aliased-route-imports/$id'
+      fullPath: '/aliased-route-imports/$id'
+      preLoaderRoute: typeof AliasedRouteImportsIdRouteImport
+      parentRoute: typeof rootRouteImport
+    }
+    '/aliased-route-imports/$id/child': {
+      id: '/aliased-route-imports/$id/child'
+      path: '/child'
+      fullPath: '/aliased-route-imports/$id/child'
+      preLoaderRoute: typeof AliasedRouteImportsIdChildRouteImport
+      parentRoute: typeof AliasedRouteImportsIdRoute
+    }
   }
 }
 
+interface AliasedRouteImportsIdRouteChildren {
+  AliasedRouteImportsIdChildRoute: typeof AliasedRouteImportsIdChildRoute
+}
+
+const AliasedRouteImportsIdRouteChildren: AliasedRouteImportsIdRouteChildren = {
+  AliasedRouteImportsIdChildRoute: AliasedRouteImportsIdChildRoute,
+}
+
+const AliasedRouteImportsIdRouteWithChildren =
+  AliasedRouteImportsIdRoute._addFileChildren(
+    AliasedRouteImportsIdRouteChildren,
+  )
+
 const rootRouteChildren: RootRouteChildren = {
   IndexRoute: IndexRoute,
   ChildRoute: ChildRoute,
@@ -271,6 +324,7 @@ const rootRouteChildren: RootRouteChildren = {
   ComponentHmrNamedSplitRoute: ComponentHmrNamedSplitRoute,
   InputsRoute: InputsRoute,
   ServerFnHmrRoute: ServerFnHmrRoute,
+  AliasedRouteImportsIdRoute: AliasedRouteImportsIdRouteWithChildren,
 }
 export const routeTree = rootRouteImport
   ._addFileChildren(rootRouteChildren)
diff --git a/e2e/react-start/hmr/src/routes/aliased-route-imports.$id.child.tsx b/e2e/react-start/hmr/src/routes/aliased-route-imports.$id.child.tsx
new file mode 100644
index 0000000000..3f95ed8715
--- /dev/null
+++ b/e2e/react-start/hmr/src/routes/aliased-route-imports.$id.child.tsx
@@ -0,0 +1,6 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { AliasedRouteImportChildPanel } from '../components/AliasedRouteImportChildPanel'
+
+export const Route = createFileRoute('/aliased-route-imports/$id/child')({
+  component: AliasedRouteImportChildPanel,
+})
diff --git a/e2e/react-start/hmr/src/routes/aliased-route-imports.$id.tsx b/e2e/react-start/hmr/src/routes/aliased-route-imports.$id.tsx
new file mode 100644
index 0000000000..5fbfbcc4e4
--- /dev/null
+++ b/e2e/react-start/hmr/src/routes/aliased-route-imports.$id.tsx
@@ -0,0 +1,6 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { AliasedRouteImportParentPanel } from '../components/AliasedRouteImportParentPanel'
+
+export const Route = createFileRoute('/aliased-route-imports/$id')({
+  component: AliasedRouteImportParentPanel,
+})
diff --git a/e2e/react-start/hmr/tests/app.spec.ts b/e2e/react-start/hmr/tests/app.spec.ts
index 2793dbd932..00c613195c 100644
--- a/e2e/react-start/hmr/tests/app.spec.ts
+++ b/e2e/react-start/hmr/tests/app.spec.ts
@@ -27,6 +27,9 @@ const routeFilePaths = {
   componentHmrNamedNosplit: 'routes/component-hmr-named-nosplit.tsx',
   componentHmrInlineErrorSplit: 'routes/component-hmr-inline-error-split.tsx',
   componentHmrNamedErrorSplit: 'routes/component-hmr-named-error-split.tsx',
+  aliasedRouteImportParentPanel:
+    'components/AliasedRouteImportParentPanel.tsx',
+  aliasedRouteImportChildPanel: 'components/AliasedRouteImportChildPanel.tsx',
   serverFnHmr: 'routes/server-fn-hmr.tsx',
   serverFnHmrFactory: 'hmr/server-fn-hmr-factory.ts',
 } as const
@@ -224,6 +227,14 @@ function normalizeRouteSource(routeFileKey: RouteFileKey, source: string) {
       'component-hmr-named-error-split-updated',
       'component-hmr-named-error-split-baseline',
     ],
+    aliasedRouteImportParentPanel: [
+      'aliased-parent-updated',
+      'aliased-parent-baseline',
+    ],
+    aliasedRouteImportChildPanel: [
+      'aliased-child-updated',
+      'aliased-child-baseline',
+    ],
   }
   const markerReplacement = markerReplacements[routeFileKey]
   if (markerReplacement) {
@@ -335,7 +346,7 @@ async function waitForServerRenderedText(
   url: string,
   text: string,
 ) {
-  const deadline = Date.now() + 20_000
+  const deadline = Date.now() + 60_000
   let lastError: unknown
 
   while (Date.now() < deadline) {
@@ -354,7 +365,7 @@ async function waitForServerRenderedText(
       lastError = error
     }
 
-    await page.waitForTimeout(150)
+    await page.waitForTimeout(100)
   }
 
   throw lastError
@@ -392,7 +403,7 @@ async function reloadPageAndWaitForText(
   testId: string,
   text: string,
 ) {
-  const deadline = Date.now() + 20_000
+  const deadline = Date.now() + 60_000
   let lastError: unknown
 
   while (Date.now() < deadline) {
@@ -451,7 +462,7 @@ async function waitForRestoredRouteFile(
         { timeout: 500 },
       )
       await restoreCheck.assert?.(page)
-      await page.waitForTimeout(500)
+      await page.waitForTimeout(100)
       await expect(page.getByTestId(restoreCheck.testId)).toHaveText(
         restoreCheck.text,
         { timeout: 1_000 },
@@ -460,7 +471,7 @@ async function waitForRestoredRouteFile(
       return
     } catch (error) {
       lastError = error
-      await page.waitForTimeout(150)
+      await page.waitForTimeout(100)
     }
   }
 
@@ -788,6 +799,69 @@ test.describe('react-start hmr', () => {
     await expect(page.getByTestId('child')).toHaveText('child')
   })
 
+  test('keeps aliased route import links working after component HMR', async ({
+    page,
+  }) => {
+    await page.goto('/aliased-route-imports/A')
+    await page.getByTestId('hydrated').waitFor({ state: 'visible' })
+
+    await expect(page.getByTestId('aliased-parent-marker')).toHaveText(
+      'aliased-parent-baseline',
+    )
+    await expect(page.getByTestId('aliased-show-child')).toHaveAttribute(
+      'href',
+      '/aliased-route-imports/A/child',
+    )
+
+    await replaceRouteTextAndWait(
+      page,
+      'aliasedRouteImportParentPanel',
+      'aliased-parent-baseline',
+      'aliased-parent-updated',
+      async () => {
+        await hmrExpect(page.getByTestId('aliased-parent-marker')).toHaveText(
+          'aliased-parent-updated',
+        )
+      },
+    )
+
+    await expect(page.getByTestId('aliased-show-child')).toHaveAttribute(
+      'href',
+      '/aliased-route-imports/A/child',
+    )
+    await page.getByTestId('aliased-show-child').click()
+    await expect(page).toHaveURL(/\/aliased-route-imports\/A\/child$/)
+
+    await page.goto('/aliased-route-imports/A/child')
+    await page.getByTestId('hydrated').waitFor({ state: 'visible' })
+    await expect(page.getByTestId('aliased-child-marker')).toHaveText(
+      'aliased-child-baseline',
+    )
+    await expect(page.getByTestId('aliased-back-parent')).toHaveAttribute(
+      'href',
+      '/aliased-route-imports/A',
+    )
+
+    await replaceRouteTextAndWait(
+      page,
+      'aliasedRouteImportChildPanel',
+      'aliased-child-baseline',
+      'aliased-child-updated',
+      async () => {
+        await hmrExpect(page.getByTestId('aliased-child-marker')).toHaveText(
+          'aliased-child-updated',
+        )
+      },
+    )
+
+    await expect(page.getByTestId('aliased-back-parent')).toHaveAttribute(
+      'href',
+      '/aliased-route-imports/A',
+    )
+    await page.getByTestId('aliased-back-parent').click()
+    await expect(page).toHaveURL(/\/aliased-route-imports\/A$/)
+  })
+
   test('clears stale beforeLoad context when beforeLoad is removed', async ({
     page,
   }) => {
diff --git a/packages/router-plugin/src/core/hmr/handle-route-update.ts b/packages/router-plugin/src/core/hmr/handle-route-update.ts
index 7ca534c31c..c0325b06e9 100644
--- a/packages/router-plugin/src/core/hmr/handle-route-update.ts
+++ b/packages/router-plugin/src/core/hmr/handle-route-update.ts
@@ -7,6 +7,7 @@ import type {
 
 type AnyRouteWithPrivateProps = AnyRoute & {
   options: Record<string, unknown>
+  parentRoute: AnyRoute
   _componentsPromise?: Promise<void>
   _lazyPromise?: Promise<void>
   update: (options: Record<string, unknown>) => unknown
@@ -109,6 +110,7 @@ function handleRouteUpdate(
   oldRoute._lazyPromise = undefined
 
   router.setRoutes(router.buildRouteTree())
+  syncHotRouteExport(oldRoute)
   router.resolvePathCache.clear()
 
   const filter = (m: AnyRouteMatch) => m.routeId === oldRoute.id
@@ -159,6 +161,18 @@ function handleRouteUpdate(
     router.invalidate({ filter, sync: true })
   }
 
+  function syncHotRouteExport(liveRoute: AnyRouteWithPrivateProps) {
+    // routeTree.gen.ts mutates the original module export with generated
+    // routing state. Mirror that state onto the fresh HMR export too, so
+    // aliased route imports keep working after the module is hot-reloaded.
+    newRoute.options = liveRoute.options
+    newRoute.parentRoute = liveRoute.parentRoute
+    newRoute._path = liveRoute._path
+    newRoute._id = liveRoute._id
+    newRoute._fullPath = liveRoute._fullPath
+    newRoute._to = liveRoute._to
+  }
+
   function getStoreMatch(matchId: string) {
     return (
       router.stores.pendingMatchStores.get(matchId)?.get() ||
diff --git a/packages/router-plugin/src/core/hmr/vite-adapter.ts b/packages/router-plugin/src/core/hmr/vite-adapter.ts
index 4519682dbb..ba8d42a80a 100644
--- a/packages/router-plugin/src/core/hmr/vite-adapter.ts
+++ b/packages/router-plugin/src/core/hmr/vite-adapter.ts
@@ -28,13 +28,30 @@ export function createViteHmrStatement(
 if (import.meta.hot) {
   const hot = import.meta.hot
   const hotData = hot.data ??= {}
+  const handleRouteUpdate = ${handleRouteUpdateCode}
+  const initialRouteId = ${routeIdFallback} ?? hotData['tsr-route-id']
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId
+  }
+  const existingRoute =
+    typeof window !== 'undefined' && initialRouteId
+      ? window.__TSR_ROUTER__?.routesById?.[initialRouteId]
+      : undefined
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route)
+    hotData['tsr-route-update-handled'] = Route
+  }
   hot.accept((newModule) => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? ${routeIdFallback}
       if (routeId) {
         hotData['tsr-route-id'] = routeId
       }
-      (${handleRouteUpdateCode})(routeId, newModule.Route)
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled']
+        return
+      }
+      handleRouteUpdate(routeId, newModule.Route)
     }
     })
 }
diff --git a/packages/router-plugin/tests/add-hmr.test.ts b/packages/router-plugin/tests/add-hmr.test.ts
index 7cfcd800d0..4430cef74c 100644
--- a/packages/router-plugin/tests/add-hmr.test.ts
+++ b/packages/router-plugin/tests/add-hmr.test.ts
@@ -139,6 +139,21 @@ describe('add-hmr works', () => {
     expect(output).toContain('/posts')
   })
 
+  it('prefers the current generated route id over stale Vite hot data', async () => {
+    const statement = createRouteHmrStatement([], {
+      hmrStyle: 'vite',
+      targetFramework: 'react',
+      routeId: '/current-route',
+    })
+    const output = JSON.stringify(statement)
+
+    expect(output).toContain('"name":"initialRouteId"')
+    expect(output).toContain(
+      '"operator":"??","left":{"type":"StringLiteral","value":"/current-route"',
+    )
+    expect(output).toContain('"object":{"type":"Identifier","name":"hotData"}')
+  })
+
   it('normalizes the generated root route id for Vite HMR', async () => {
     const statement = createRouteHmrStatement([], {
       hmrStyle: 'vite',
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx
index 87b7af6c67..a463d9ab22 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@true.tsx
@@ -38,133 +38,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx
index 6466a24285..44908e32a9 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/arrow-function@webpack-hot.tsx
@@ -66,6 +66,7 @@ if (import.meta.webpackHot) {
       oldRoute._componentsPromise = undefined;
       oldRoute._lazyPromise = undefined;
       router.setRoutes(router.buildRouteTree());
+      syncHotRouteExport(oldRoute);
       router.resolvePathCache.clear();
       const filter = m => m.routeId === oldRoute.id;
       const activeMatch = router.stores.matches.get().find(filter);
@@ -104,6 +105,14 @@ if (import.meta.webpackHot) {
         });
       }
       ;
+      function syncHotRouteExport(liveRoute) {
+        newRoute.options = liveRoute.options;
+        newRoute.parentRoute = liveRoute.parentRoute;
+        newRoute._path = liveRoute._path;
+        newRoute._id = liveRoute._id;
+        newRoute._fullPath = liveRoute._fullPath;
+        newRoute._to = liveRoute._to;
+      }
       function getStoreMatch(matchId) {
         return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
       }
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx
index 146ad344b4..9ef182f4b7 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute-inline-component@true.tsx
@@ -27,133 +27,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx
index 098bc0d7dc..9789370efc 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRoute@true.tsx
@@ -29,133 +29,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx
index 877680a18b..6c7073a8f3 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/createRootRouteWithContext-type-args@true.tsx
@@ -32,133 +32,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx
index ba595db5bb..bee793d372 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/explicit-undefined-component@true.tsx
@@ -26,133 +26,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx
index 1506201d77..75b457b778 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/function-declaration@true.tsx
@@ -38,133 +38,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx
index 46f08ed686..7f28b7b3d3 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/multi-component@true.tsx
@@ -48,133 +48,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx
index ca91b0a8f2..6bee992948 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/react/string-literal-keys@true.tsx
@@ -48,133 +48,156 @@ export function TSRFastRefreshAnchor() {
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = ["component", "shellComponent", "pendingComponent", "errorComponent", "notFoundComponent"];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx b/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx
index ea83b4812f..2d1be7e32b 100644
--- a/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx
+++ b/packages/router-plugin/tests/add-hmr/snapshots/solid/arrow-function@true.tsx
@@ -9,133 +9,156 @@ export const Route = createFileRoute('/posts')({
 if (import.meta.hot) {
   const hot = import.meta.hot;
   const hotData = hot.data ??= {};
+  const handleRouteUpdate = function handleRouteUpdate(routeId, newRoute) {
+    const router = window.__TSR_ROUTER__;
+    const oldRoute = router.routesById[routeId];
+    if (!oldRoute) {
+      return;
+    }
+    ;
+    const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
+    const generatedRouteOptions = {};
+    generatedRouteOptionKeys.forEach(key => {
+      if (key in oldRoute.options) {
+        generatedRouteOptions[key] = oldRoute.options[key];
+      }
+    });
+    const removedKeys = new Set();
+    Object.keys(oldRoute.options).forEach(key => {
+      if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
+        removedKeys.add(key);
+        delete oldRoute.options[key];
+      }
+    });
+    const oldHasShellComponent = "shellComponent" in oldRoute.options;
+    const newHasShellComponent = "shellComponent" in newRoute.options;
+    const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
+    const componentKeys = [];
+    if (preserveComponentIdentity) {
+      componentKeys.forEach(key => {
+        if (key in oldRoute.options && key in newRoute.options) {
+          newRoute.options[key] = oldRoute.options[key];
+        }
+      });
+    }
+    ;
+    const nextOptions = {
+      ...newRoute.options,
+      ...generatedRouteOptions
+    };
+    oldRoute.options = nextOptions;
+    oldRoute.update(nextOptions);
+    oldRoute._componentsPromise = undefined;
+    oldRoute._lazyPromise = undefined;
+    router.setRoutes(router.buildRouteTree());
+    syncHotRouteExport(oldRoute);
+    router.resolvePathCache.clear();
+    const filter = m => m.routeId === oldRoute.id;
+    const activeMatch = router.stores.matches.get().find(filter);
+    const pendingMatch = router.stores.pendingMatches.get().find(filter);
+    const cachedMatches = router.stores.cachedMatches.get().filter(filter);
+    if (activeMatch || pendingMatch || cachedMatches.length > 0) {
+      if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
+        const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
+        router.batch(() => {
+          for (const matchId of matchIds) {
+            const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
+            if (store) {
+              store.set(prev => {
+                const next = {
+                  ...prev
+                };
+                if (removedKeys.has("loader")) {
+                  next.loaderData = undefined;
+                }
+                ;
+                if (removedKeys.has("beforeLoad")) {
+                  next.__beforeLoadContext = undefined;
+                  next.context = rebuildMatchContextWithoutBeforeLoad(next);
+                }
+                ;
+                return next;
+              });
+            }
+          }
+        });
+      }
+      ;
+      router.invalidate({
+        filter,
+        sync: true
+      });
+    }
+    ;
+    function syncHotRouteExport(liveRoute) {
+      newRoute.options = liveRoute.options;
+      newRoute.parentRoute = liveRoute.parentRoute;
+      newRoute._path = liveRoute._path;
+      newRoute._id = liveRoute._id;
+      newRoute._fullPath = liveRoute._fullPath;
+      newRoute._to = liveRoute._to;
+    }
+    function getStoreMatch(matchId) {
+      return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
+    }
+    function getMatchList(matchId) {
+      const pendingMatches = router.stores.pendingMatches.get();
+      if (pendingMatches.some(match => match.id === matchId)) {
+        return pendingMatches;
+      }
+      ;
+      const activeMatches = router.stores.matches.get();
+      if (activeMatches.some(match => match.id === matchId)) {
+        return activeMatches;
+      }
+      ;
+      const cachedMatches = router.stores.cachedMatches.get();
+      if (cachedMatches.some(match => match.id === matchId)) {
+        return cachedMatches;
+      }
+      ;
+      return [];
+    }
+    function getParentMatch(match) {
+      const matchList = getMatchList(match.id);
+      const matchIndex = matchList.findIndex(item => item.id === match.id);
+      if (matchIndex <= 0) {
+        return undefined;
+      }
+      ;
+      const parentMatch = matchList[matchIndex - 1];
+      return getStoreMatch(parentMatch.id) || parentMatch;
+    }
+    function rebuildMatchContextWithoutBeforeLoad(match) {
+      const parentMatch = getParentMatch(match);
+      const getParentContext = router.getParentContext;
+      const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
+      return {
+        ...(parentContext ?? {}),
+        ...(match.__routeContext ?? {})
+      };
+    }
+  };
+  const initialRouteId = Route.id ?? hotData['tsr-route-id'];
+  if (initialRouteId) {
+    hotData['tsr-route-id'] = initialRouteId;
+  }
+  const existingRoute = typeof window !== 'undefined' && initialRouteId ? window.__TSR_ROUTER__?.routesById?.[initialRouteId] : undefined;
+  if (initialRouteId && existingRoute && existingRoute !== Route) {
+    handleRouteUpdate(initialRouteId, Route);
+    hotData['tsr-route-update-handled'] = Route;
+  }
   hot.accept(newModule => {
     if (Route && newModule && newModule.Route) {
       const routeId = hotData['tsr-route-id'] ?? Route.id;
       if (routeId) {
         hotData['tsr-route-id'] = routeId;
       }
-      (function handleRouteUpdate(routeId, newRoute) {
-        const router = window.__TSR_ROUTER__;
-        const oldRoute = router.routesById[routeId];
-        if (!oldRoute) {
-          return;
-        }
-        ;
-        const generatedRouteOptionKeys = new Set(["id", "path", "getParentRoute"]);
-        const generatedRouteOptions = {};
-        generatedRouteOptionKeys.forEach(key => {
-          if (key in oldRoute.options) {
-            generatedRouteOptions[key] = oldRoute.options[key];
-          }
-        });
-        const removedKeys = new Set();
-        Object.keys(oldRoute.options).forEach(key => {
-          if (!generatedRouteOptionKeys.has(key) && !(key in newRoute.options)) {
-            removedKeys.add(key);
-            delete oldRoute.options[key];
-          }
-        });
-        const oldHasShellComponent = "shellComponent" in oldRoute.options;
-        const newHasShellComponent = "shellComponent" in newRoute.options;
-        const preserveComponentIdentity = oldHasShellComponent === newHasShellComponent;
-        const componentKeys = [];
-        if (preserveComponentIdentity) {
-          componentKeys.forEach(key => {
-            if (key in oldRoute.options && key in newRoute.options) {
-              newRoute.options[key] = oldRoute.options[key];
-            }
-          });
-        }
-        ;
-        const nextOptions = {
-          ...newRoute.options,
-          ...generatedRouteOptions
-        };
-        oldRoute.options = nextOptions;
-        oldRoute.update(nextOptions);
-        oldRoute._componentsPromise = undefined;
-        oldRoute._lazyPromise = undefined;
-        router.setRoutes(router.buildRouteTree());
-        router.resolvePathCache.clear();
-        const filter = m => m.routeId === oldRoute.id;
-        const activeMatch = router.stores.matches.get().find(filter);
-        const pendingMatch = router.stores.pendingMatches.get().find(filter);
-        const cachedMatches = router.stores.cachedMatches.get().filter(filter);
-        if (activeMatch || pendingMatch || cachedMatches.length > 0) {
-          if (removedKeys.has("loader") || removedKeys.has("beforeLoad")) {
-            const matchIds = [activeMatch?.id, pendingMatch?.id, ...cachedMatches.map(match => match.id)].filter(Boolean);
-            router.batch(() => {
-              for (const matchId of matchIds) {
-                const store = router.stores.pendingMatchStores.get(matchId) || router.stores.matchStores.get(matchId) || router.stores.cachedMatchStores.get(matchId);
-                if (store) {
-                  store.set(prev => {
-                    const next = {
-                      ...prev
-                    };
-                    if (removedKeys.has("loader")) {
-                      next.loaderData = undefined;
-                    }
-                    ;
-                    if (removedKeys.has("beforeLoad")) {
-                      next.__beforeLoadContext = undefined;
-                      next.context = rebuildMatchContextWithoutBeforeLoad(next);
-                    }
-                    ;
-                    return next;
-                  });
-                }
-              }
-            });
-          }
-          ;
-          router.invalidate({
-            filter,
-            sync: true
-          });
-        }
-        ;
-        function getStoreMatch(matchId) {
-          return router.stores.pendingMatchStores.get(matchId)?.get() || router.stores.matchStores.get(matchId)?.get() || router.stores.cachedMatchStores.get(matchId)?.get();
-        }
-        function getMatchList(matchId) {
-          const pendingMatches = router.stores.pendingMatches.get();
-          if (pendingMatches.some(match => match.id === matchId)) {
-            return pendingMatches;
-          }
-          ;
-          const activeMatches = router.stores.matches.get();
-          if (activeMatches.some(match => match.id === matchId)) {
-            return activeMatches;
-          }
-          ;
-          const cachedMatches = router.stores.cachedMatches.get();
-          if (cachedMatches.some(match => match.id === matchId)) {
-            return cachedMatches;
-          }
-          ;
-          return [];
-        }
-        function getParentMatch(match) {
-          const matchList = getMatchList(match.id);
-          const matchIndex = matchList.findIndex(item => item.id === match.id);
-          if (matchIndex <= 0) {
-            return undefined;
-          }
-          ;
-          const parentMatch = matchList[matchIndex - 1];
-          return getStoreMatch(parentMatch.id) || parentMatch;
-        }
-        function rebuildMatchContextWithoutBeforeLoad(match) {
-          const parentMatch = getParentMatch(match);
-          const getParentContext = router.getParentContext;
-          const parentContext = getParentContext ? getParentContext.call(router, parentMatch) : parentMatch?.context ?? router.options.context;
-          return {
-            ...(parentContext ?? {}),
-            ...(match.__routeContext ?? {})
-          };
-        }
-      })(routeId, newModule.Route);
+      if (hotData['tsr-route-update-handled'] === newModule.Route) {
+        delete hotData['tsr-route-update-handled'];
+        return;
+      }
+      handleRouteUpdate(routeId, newModule.Route);
     }
   });
 }
\ No newline at end of file
diff --git a/packages/router-plugin/tests/handle-route-update.test.ts b/packages/router-plugin/tests/handle-route-update.test.ts
index 8f1b28cdb6..9a80bfbeb4 100644
--- a/packages/router-plugin/tests/handle-route-update.test.ts
+++ b/packages/router-plugin/tests/handle-route-update.test.ts
@@ -190,4 +190,31 @@ describe('handleRouteUpdate', () => {
       itemRoute.id,
     )
   })
+
+  it('hydrates the hot module route export with generated route tree state', () => {
+    const rootRoute = new BaseRootRoute({})
+    const itemRoute = new BaseRoute({
+      getParentRoute: () => rootRoute,
+      path: '/items/$itemId',
+    })
+    const routeTree = rootRoute.addChildren([itemRoute])
+    const router = createTestRouter(routeTree)
+    const newRoute = new BaseRoute({} as any)
+
+    expect((newRoute as any).to).toBeUndefined()
+
+    const restoreWindow = withWindowRouter(router)
+    try {
+      getHandleRouteUpdate()(itemRoute.id, newRoute)
+    } finally {
+      restoreWindow()
+    }
+
+    expect(newRoute.id).toBe(itemRoute.id)
+    expect(newRoute.path).toBe(itemRoute.path)
+    expect(newRoute.fullPath).toBe(itemRoute.fullPath)
+    expect(newRoute.to).toBe(itemRoute.to)
+    expect((newRoute as any).parentRoute).toBe(rootRoute)
+    expect((newRoute as any).options).toBe((itemRoute as any).options)
+  })
 })

From 83e7c8d927152e4ba2698643c5610a2969e422ed Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Sat, 6 Jun 2026 08:21:44 +0000
Subject: [PATCH 2/2] ci: apply automated fixes

---
 e2e/react-start/hmr/tests/app.spec.ts | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/e2e/react-start/hmr/tests/app.spec.ts b/e2e/react-start/hmr/tests/app.spec.ts
index 00c613195c..10499e4b88 100644
--- a/e2e/react-start/hmr/tests/app.spec.ts
+++ b/e2e/react-start/hmr/tests/app.spec.ts
@@ -27,8 +27,7 @@ const routeFilePaths = {
   componentHmrNamedNosplit: 'routes/component-hmr-named-nosplit.tsx',
   componentHmrInlineErrorSplit: 'routes/component-hmr-inline-error-split.tsx',
   componentHmrNamedErrorSplit: 'routes/component-hmr-named-error-split.tsx',
-  aliasedRouteImportParentPanel:
-    'components/AliasedRouteImportParentPanel.tsx',
+  aliasedRouteImportParentPanel: 'components/AliasedRouteImportParentPanel.tsx',
   aliasedRouteImportChildPanel: 'components/AliasedRouteImportChildPanel.tsx',
   serverFnHmr: 'routes/server-fn-hmr.tsx',
   serverFnHmrFactory: 'hmr/server-fn-hmr-factory.ts',
