diff --git a/packages/react-router/src/ScriptOnce.tsx b/packages/react-router/src/ScriptOnce.tsx
index f00317d52fb..97609c6fc7b 100644
--- a/packages/react-router/src/ScriptOnce.tsx
+++ b/packages/react-router/src/ScriptOnce.tsx
@@ -12,9 +12,8 @@ export function ScriptOnce({ children }: { children: string }) {
return (
)
diff --git a/packages/react-start-client/src/StartClient.tsx b/packages/react-start-client/src/StartClient.tsx
index e35b5942301..899ae05fb2b 100644
--- a/packages/react-start-client/src/StartClient.tsx
+++ b/packages/react-start-client/src/StartClient.tsx
@@ -1,6 +1,6 @@
import { Await, RouterProvider } from '@tanstack/react-router'
-import { hydrateStart } from '@tanstack/start-client-core/client'
+import { hydrateStart } from './hydrateStart'
import type { AnyRouter } from '@tanstack/router-core'
diff --git a/packages/react-start-client/src/hydrateStart.ts b/packages/react-start-client/src/hydrateStart.ts
new file mode 100644
index 00000000000..22f4848de7d
--- /dev/null
+++ b/packages/react-start-client/src/hydrateStart.ts
@@ -0,0 +1,12 @@
+import { hydrateStart as coreHydrateStart } from '@tanstack/start-client-core/client'
+import type { AnyRouter } from '@tanstack/router-core'
+
+/**
+ * React-specific wrapper for hydrateStart that signals hydration completion
+ */
+export async function hydrateStart(): Promise {
+ const router = await coreHydrateStart()
+ // Signal that router hydration is complete so cleanup can happen if stream has ended
+ window.$_TSR?.h()
+ return router
+}
diff --git a/packages/react-start-client/src/index.tsx b/packages/react-start-client/src/index.tsx
index 8c20b13c34f..6c8a836f086 100644
--- a/packages/react-start-client/src/index.tsx
+++ b/packages/react-start-client/src/index.tsx
@@ -1,2 +1,3 @@
export { StartClient } from './StartClient'
export { renderRsc } from './renderRSC'
+export { hydrateStart } from './hydrateStart'
diff --git a/packages/react-start-client/tsconfig.json b/packages/react-start-client/tsconfig.json
index 2fbc4be857e..51dda9abf2a 100644
--- a/packages/react-start-client/tsconfig.json
+++ b/packages/react-start-client/tsconfig.json
@@ -4,9 +4,5 @@
"jsx": "react-jsx",
"module": "esnext"
},
- "include": [
- "src",
- "vite.config.ts",
- "../start-client-core/src/startEntry.d.ts"
- ]
+ "include": ["src", "vite.config.ts"]
}
diff --git a/packages/router-core/src/ssr/client.ts b/packages/router-core/src/ssr/client.ts
index ab426336017..91499e22c96 100644
--- a/packages/router-core/src/ssr/client.ts
+++ b/packages/router-core/src/ssr/client.ts
@@ -3,3 +3,4 @@ export { json } from './json'
export type { JsonResponse } from './json'
export { hydrate } from './ssr-client'
export * from './ssr-client'
+export type { TsrSsrGlobal, DehydratedMatch, DehydratedRouter } from './types'
diff --git a/packages/router-core/src/ssr/ssr-client.ts b/packages/router-core/src/ssr/ssr-client.ts
index 1e9f54be734..a58e1041d58 100644
--- a/packages/router-core/src/ssr/ssr-client.ts
+++ b/packages/router-core/src/ssr/ssr-client.ts
@@ -2,41 +2,20 @@ import invariant from 'tiny-invariant'
import { batch } from '@tanstack/store'
import { isNotFound } from '../not-found'
import { createControlledPromise } from '../utils'
-import type { AnyRouteMatch, MakeRouteMatch } from '../Matches'
+import type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'
+import type { DehydratedMatch, TsrSsrGlobal } from './types'
+import type { AnyRouteMatch } from '../Matches'
import type { AnyRouter } from '../router'
-import type { Manifest } from '../manifest'
import type { RouteContextOptions } from '../route'
import type { AnySerializationAdapter } from './serializer/transformer'
-import type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'
declare global {
interface Window {
[GLOBAL_TSR]?: TsrSsrGlobal
[GLOBAL_SEROVAL]?: any
- // Vue sets this to true before TSR scripts run to defer cleanup until after hydration
- $_TSR_DEFER?: boolean
}
}
-export interface TsrSsrGlobal {
- router?: DehydratedRouter
- // clean scripts; shortened since this is sent for each streamed script
- c: () => void
- // push script into buffer; shortened since this is sent for each streamed script as soon as the first custom transformer was invoked
- p: (script: () => void) => void
- buffer: Array<() => void>
- // custom transformers, shortened since this is sent for each streamed value that needs a custom transformer
- t?: Map any>
- // this flag indicates whether the transformers were initialized
- initialized?: boolean
- // router is hydrated and doesnt need the streamed values anymore
- hydrated?: boolean
- // stream has ended
- streamEnd?: boolean
- // called by Vue after hydration to perform deferred cleanup
- cleanup?: () => void
-}
-
function hydrateMatch(
match: AnyRouteMatch,
deyhydratedMatch: DehydratedMatch,
@@ -49,22 +28,6 @@ function hydrateMatch(
match.updatedAt = deyhydratedMatch.u
match.error = deyhydratedMatch.e
}
-export interface DehydratedMatch {
- i: MakeRouteMatch['id']
- b?: MakeRouteMatch['__beforeLoadContext']
- l?: MakeRouteMatch['loaderData']
- e?: MakeRouteMatch['error']
- u: MakeRouteMatch['updatedAt']
- s: MakeRouteMatch['status']
- ssr?: MakeRouteMatch['ssr']
-}
-
-export interface DehydratedRouter {
- manifest: Manifest | undefined
- dehydratedData?: any
- lastMatchId?: string
- matches: Array
-}
export async function hydrate(router: AnyRouter): Promise {
invariant(
@@ -183,10 +146,6 @@ export async function hydrate(router: AnyRouter): Promise {
// Allow the user to handle custom hydration data
await router.options.hydrate?.(dehydratedData)
- window.$_TSR.hydrated = true
- // potentially clean up streamed values IF stream has ended already
- window.$_TSR.c()
-
// now that all necessary data is hydrated:
// 1) fully reconstruct the route context
// 2) execute `head()` and `scripts()` for each match
diff --git a/packages/router-core/src/ssr/ssr-server.ts b/packages/router-core/src/ssr/ssr-server.ts
index 85d03824983..fcaf7bd1550 100644
--- a/packages/router-core/src/ssr/ssr-server.ts
+++ b/packages/router-core/src/ssr/ssr-server.ts
@@ -6,10 +6,9 @@ import { GLOBAL_TSR } from './constants'
import { defaultSerovalPlugins } from './serializer/seroval-plugins'
import { makeSsrSerovalPlugin } from './serializer/transformer'
import { TSR_SCRIPT_BARRIER_ID } from './transformStreamWithRouter'
+import type { DehydratedMatch, DehydratedRouter } from './types'
import type { AnySerializationAdapter } from './serializer/transformer'
import type { AnyRouter } from '../router'
-import type { DehydratedMatch } from './ssr-client'
-import type { DehydratedRouter } from './client'
import type { AnyRouteMatch } from '../Matches'
import type { Manifest, RouterManagedTag } from '../manifest'
@@ -91,7 +90,7 @@ class ScriptBuffer {
if (bufferedScripts.length === 0) {
return undefined
}
- bufferedScripts.push(`${GLOBAL_TSR}.c()`)
+ bufferedScripts.push(`document.currentScript.remove()`)
const joinedScripts = bufferedScripts.join(';')
return joinedScripts
}
@@ -143,7 +142,7 @@ export function attachRouterServerSsrUtils({
if (!script) {
return ''
}
- return ``
+ return ``
})
},
dehydrate: async () => {
@@ -223,7 +222,7 @@ export function attachRouterServerSsrUtils({
},
scopeId: SCOPE_ID,
onDone: () => {
- scriptBuffer.enqueue(GLOBAL_TSR + '.streamEnd=true')
+ scriptBuffer.enqueue(GLOBAL_TSR + '.e()')
p.resolve('')
},
onError: (err) => p.reject(err),
diff --git a/packages/router-core/src/ssr/tsrScript.ts b/packages/router-core/src/ssr/tsrScript.ts
index 084638cfa4c..69c9abe05d5 100644
--- a/packages/router-core/src/ssr/tsrScript.ts
+++ b/packages/router-core/src/ssr/tsrScript.ts
@@ -1,25 +1,15 @@
self.$_TSR = {
- c() {
- // If Vue has set the defer flag, don't remove scripts yet - wait for Vue to call cleanup()
- if (self.$_TSR_DEFER) {
- return
- }
- document.querySelectorAll('.\\$tsr').forEach((o) => {
- o.remove()
- })
- if (this.hydrated && this.streamEnd) {
- delete self.$_TSR
- delete self.$R['tsr']
- }
+ h() {
+ this.hydrated = true
+ this.c()
+ },
+ e() {
+ this.streamEnded = true
+ this.c()
},
- // Called by Vue after hydration is complete to perform deferred cleanup
- cleanup() {
- document.querySelectorAll('.\\$tsr').forEach((o) => {
- o.remove()
- })
- if (this.hydrated && this.streamEnd) {
+ c() {
+ if (this.hydrated && this.streamEnded) {
delete self.$_TSR
- delete self.$_TSR_DEFER
delete self.$R['tsr']
}
},
diff --git a/packages/router-core/src/ssr/types.ts b/packages/router-core/src/ssr/types.ts
new file mode 100644
index 00000000000..ec0feee41a4
--- /dev/null
+++ b/packages/router-core/src/ssr/types.ts
@@ -0,0 +1,40 @@
+import type { Manifest } from '../manifest'
+import type { MakeRouteMatch } from '../Matches'
+
+export interface DehydratedMatch {
+ i: MakeRouteMatch['id']
+ b?: MakeRouteMatch['__beforeLoadContext']
+ l?: MakeRouteMatch['loaderData']
+ e?: MakeRouteMatch['error']
+ u: MakeRouteMatch['updatedAt']
+ s: MakeRouteMatch['status']
+ ssr?: MakeRouteMatch['ssr']
+}
+
+export interface DehydratedRouter {
+ manifest: Manifest | undefined
+ dehydratedData?: any
+ lastMatchId?: string
+ matches: Array
+}
+
+export interface TsrSsrGlobal {
+ router?: DehydratedRouter
+ // Signal that router hydration is complete
+ h: () => void
+ // Signal that stream has ended
+ e: () => void
+ // Cleanup all hydration resources and scripts
+ c: () => void
+ // p: Push script into buffer or execute immediately
+ p: (script: () => void) => void
+ buffer: Array<() => void>
+ // custom transformers, shortened since this is sent for each streamed value that needs a custom transformer
+ t?: Map any>
+ // this flag indicates whether the transformers were initialized
+ initialized?: boolean
+ // router is hydrated and doesnt need the streamed values anymore
+ hydrated?: boolean
+ // stream has ended
+ streamEnded?: boolean
+}
diff --git a/packages/react-router/tests/hydrate.test.ts b/packages/router-core/tests/hydrate.test.ts
similarity index 91%
rename from packages/react-router/tests/hydrate.test.ts
rename to packages/router-core/tests/hydrate.test.ts
index b0d91530554..101ade6f571 100644
--- a/packages/react-router/tests/hydrate.test.ts
+++ b/packages/router-core/tests/hydrate.test.ts
@@ -1,13 +1,8 @@
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
+import { createMemoryHistory } from '@tanstack/history'
import { hydrate } from '@tanstack/router-core/ssr/client'
-import {
- createMemoryHistory,
- createRootRoute,
- createRoute,
- createRouter,
- notFound,
-} from '../src'
-import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client'
+import { BaseRootRoute, BaseRoute, RouterCore, notFound } from '../src'
+import type { TsrSsrGlobal } from '../src/ssr/types'
import type { AnyRouteMatch } from '../src'
describe('hydrate', () => {
@@ -25,9 +20,9 @@ describe('hydrate', () => {
const history = createMemoryHistory({ initialEntries: ['/'] })
- const rootRoute = createRootRoute({})
+ const rootRoute = new BaseRootRoute({})
- const indexRoute = createRoute({
+ const indexRoute = new BaseRoute({
getParentRoute: () => rootRoute,
path: '/',
component: () => 'Index',
@@ -35,7 +30,7 @@ describe('hydrate', () => {
head: mockHead,
})
- const otherRoute = createRoute({
+ const otherRoute = new BaseRoute({
getParentRoute: () => indexRoute,
path: '/other',
component: () => 'Other',
@@ -45,7 +40,7 @@ describe('hydrate', () => {
indexRoute.addChildren([otherRoute]),
])
- mockRouter = createRouter({ routeTree, history, isServer: true })
+ mockRouter = new RouterCore({ routeTree, history, isServer: true })
})
afterEach(() => {
@@ -100,6 +95,8 @@ describe('hydrate', () => {
lastMatchId: '/',
matches: [],
},
+ h: vi.fn(),
+ e: vi.fn(),
c: vi.fn(),
p: vi.fn(),
buffer: mockBuffer,
@@ -127,6 +124,8 @@ describe('hydrate', () => {
lastMatchId: '/',
matches: [],
},
+ h: vi.fn(),
+ e: vi.fn(),
c: vi.fn(),
p: vi.fn(),
buffer: [],
@@ -148,6 +147,8 @@ describe('hydrate', () => {
lastMatchId: '/',
matches: [],
},
+ h: vi.fn(),
+ e: vi.fn(),
c: vi.fn(),
p: vi.fn(),
buffer: [],
@@ -199,6 +200,8 @@ describe('hydrate', () => {
lastMatchId: '/',
matches: dehydratedMatches,
},
+ h: vi.fn(),
+ e: vi.fn(),
c: vi.fn(),
p: vi.fn(),
buffer: [],
@@ -242,6 +245,8 @@ describe('hydrate', () => {
},
],
},
+ h: vi.fn(),
+ e: vi.fn(),
c: vi.fn(),
p: vi.fn(),
buffer: [],
diff --git a/packages/solid-router/src/ScriptOnce.tsx b/packages/solid-router/src/ScriptOnce.tsx
index 572570c8ddd..1ad12ccb071 100644
--- a/packages/solid-router/src/ScriptOnce.tsx
+++ b/packages/solid-router/src/ScriptOnce.tsx
@@ -15,7 +15,7 @@ export function ScriptOnce({
)
}
diff --git a/packages/solid-router/tests/hydrate.test.ts b/packages/solid-router/tests/hydrate.test.ts
deleted file mode 100644
index d71e29d7e98..00000000000
--- a/packages/solid-router/tests/hydrate.test.ts
+++ /dev/null
@@ -1,265 +0,0 @@
-import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
-import { hydrate } from '@tanstack/router-core/ssr/client'
-import {
- createMemoryHistory,
- createRootRoute,
- createRoute,
- createRouter,
- notFound,
-} from '../src'
-import type { AnyRouteMatch } from '../src'
-import type { TsrSsrGlobal } from '@tanstack/router-core/ssr/client'
-
-describe('hydrate', () => {
- let mockWindow: { $_TSR?: TsrSsrGlobal }
- let mockRouter: any
- let mockHead: any
-
- beforeEach(() => {
- // Reset global window mock
- mockWindow = {}
- ;(global as any).window = mockWindow
-
- // Reset mock head function
- mockHead = vi.fn()
-
- const history = createMemoryHistory({ initialEntries: ['/'] })
-
- const rootRoute = createRootRoute({})
-
- const indexRoute = createRoute({
- getParentRoute: () => rootRoute,
- path: '/',
- component: () => 'Index',
- notFoundComponent: () => 'Not Found',
- head: mockHead,
- })
-
- const otherRoute = createRoute({
- getParentRoute: () => indexRoute,
- path: '/other',
- component: () => 'Other',
- })
-
- const routeTree = rootRoute.addChildren([
- indexRoute.addChildren([otherRoute]),
- ])
-
- mockRouter = createRouter({ routeTree, history, isServer: true })
- })
-
- afterEach(() => {
- vi.resetAllMocks()
- delete (global as any).window
- })
-
- it('should throw error if window.$_TSR is not available', async () => {
- await expect(hydrate(mockRouter)).rejects.toThrow(
- 'Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!',
- )
- })
-
- it('should throw error if window.$_TSR.router is not available', async () => {
- mockWindow.$_TSR = {
- c: vi.fn(),
- p: vi.fn(),
- buffer: [],
- initialized: false,
- // router is missing
- } as any
-
- await expect(hydrate(mockRouter)).rejects.toThrow(
- 'Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!',
- )
- })
-
- it('should initialize serialization adapters when provided', async () => {
- const mockSerializer = {
- key: 'testAdapter',
- fromSerializable: vi.fn(),
- toSerializable: vi.fn(),
- test: vi.fn().mockReturnValue(true),
- '~types': {
- input: {},
- output: {},
- extends: {},
- },
- }
-
- mockRouter.options.serializationAdapters = [mockSerializer]
-
- const mockMatches = [{ id: '/', routeId: '/', index: 0, _nonReactive: {} }]
- mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches)
- mockRouter.state.matches = mockMatches
-
- const mockBuffer = [vi.fn(), vi.fn()]
- mockWindow.$_TSR = {
- router: {
- manifest: { routes: {} },
- dehydratedData: {},
- lastMatchId: '/',
- matches: [],
- },
- c: vi.fn(),
- p: vi.fn(),
- buffer: mockBuffer,
- initialized: false,
- }
-
- await hydrate(mockRouter)
-
- expect(mockWindow.$_TSR.t).toBeInstanceOf(Map)
- expect(mockWindow.$_TSR.t?.get('testAdapter')).toBe(
- mockSerializer.fromSerializable,
- )
- expect(mockBuffer[0]).toHaveBeenCalled()
- expect(mockBuffer[1]).toHaveBeenCalled()
- expect(mockWindow.$_TSR.initialized).toBe(true)
- })
-
- it('should handle empty serialization adapters', async () => {
- mockRouter.options.serializationAdapters = []
-
- mockWindow.$_TSR = {
- router: {
- manifest: { routes: {} },
- dehydratedData: {},
- lastMatchId: '/',
- matches: [],
- },
- c: vi.fn(),
- p: vi.fn(),
- buffer: [],
- initialized: false,
- }
-
- await hydrate(mockRouter)
-
- expect(mockWindow.$_TSR.t).toBeUndefined()
- expect(mockWindow.$_TSR.initialized).toBe(true)
- })
-
- it('should set manifest in router.ssr', async () => {
- const testManifest = { routes: {} }
- mockWindow.$_TSR = {
- router: {
- manifest: testManifest,
- dehydratedData: {},
- lastMatchId: '/',
- matches: [],
- },
- c: vi.fn(),
- p: vi.fn(),
- buffer: [],
- initialized: false,
- }
-
- await hydrate(mockRouter)
-
- expect(mockRouter.ssr).toEqual({
- manifest: testManifest,
- })
- })
-
- it('should hydrate matches', async () => {
- const mockMatches = [
- {
- id: '/',
- routeId: '/',
- index: 0,
- ssr: undefined,
- _nonReactive: {},
- },
- {
- id: '/other',
- routeId: '/other',
- index: 1,
- ssr: undefined,
- _nonReactive: {},
- },
- ]
-
- const dehydratedMatches = [
- {
- i: '/',
- l: { indexData: 'server-data' },
- s: 'success' as const,
- ssr: true,
- u: Date.now(),
- },
- ]
-
- mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches)
- mockRouter.state.matches = mockMatches
-
- mockWindow.$_TSR = {
- router: {
- manifest: { routes: {} },
- dehydratedData: {},
- lastMatchId: '/',
- matches: dehydratedMatches,
- },
- c: vi.fn(),
- p: vi.fn(),
- buffer: [],
- initialized: false,
- }
-
- await hydrate(mockRouter)
-
- const { id, loaderData, ssr, status } = mockMatches[0] as AnyRouteMatch
- expect(id).toBe('/')
- expect(loaderData).toEqual({ indexData: 'server-data' })
- expect(status).toBe('success')
- expect(ssr).toBe(true)
- })
-
- it('should handle errors during route context hydration', async () => {
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {})
- mockHead.mockImplementation(() => {
- throw notFound()
- })
-
- const mockMatches = [
- { id: '/', routeId: '/', index: 0, ssr: true, _nonReactive: {} },
- ]
-
- mockRouter.matchRoutes = vi.fn().mockReturnValue(mockMatches)
- mockRouter.state.matches = mockMatches
-
- mockWindow.$_TSR = {
- router: {
- manifest: { routes: {} },
- dehydratedData: {},
- lastMatchId: '/',
- matches: [
- {
- i: '/',
- l: { data: 'test' },
- s: 'success',
- ssr: true,
- u: Date.now(),
- },
- ],
- },
- c: vi.fn(),
- p: vi.fn(),
- buffer: [],
- initialized: false,
- }
-
- await hydrate(mockRouter)
-
- const match = mockRouter.state.matches[0] as AnyRouteMatch
- expect(match.error).toEqual({ isNotFound: true })
-
- expect(consoleSpy).toHaveBeenCalledWith(
- 'NotFound error during hydration for routeId: /',
- expect.objectContaining({
- isNotFound: true,
- }),
- )
-
- consoleSpy.mockRestore()
- })
-})
diff --git a/packages/solid-start-client/src/hydrateStart.ts b/packages/solid-start-client/src/hydrateStart.ts
new file mode 100644
index 00000000000..e4299cb9c72
--- /dev/null
+++ b/packages/solid-start-client/src/hydrateStart.ts
@@ -0,0 +1,12 @@
+import { hydrateStart as coreHydrateStart } from '@tanstack/start-client-core/client'
+import type { AnyRouter } from '@tanstack/router-core'
+
+/**
+ * Solid-specific wrapper for hydrateStart that signals hydration completion
+ */
+export async function hydrateStart(): Promise {
+ const router = await coreHydrateStart()
+ // Signal that router hydration is complete so cleanup can happen if stream has ended
+ window.$_TSR?.h()
+ return router
+}
diff --git a/packages/solid-start-client/src/index.tsx b/packages/solid-start-client/src/index.tsx
index bb2953e2e35..aa73990a576 100644
--- a/packages/solid-start-client/src/index.tsx
+++ b/packages/solid-start-client/src/index.tsx
@@ -1,2 +1,2 @@
export { StartClient } from './StartClient'
-export { hydrateStart } from '@tanstack/start-client-core/client'
+export { hydrateStart } from './hydrateStart'
diff --git a/packages/start-client-core/src/client/index.ts b/packages/start-client-core/src/client/index.ts
index b3d9a44b11c..e1b92bd78cf 100644
--- a/packages/start-client-core/src/client/index.ts
+++ b/packages/start-client-core/src/client/index.ts
@@ -1 +1,2 @@
export { hydrateStart } from './hydrateStart'
+export type * from '@tanstack/router-core/ssr/client'
diff --git a/packages/start-client-core/src/index.tsx b/packages/start-client-core/src/index.tsx
index 70a4692842a..015ef25987d 100644
--- a/packages/start-client-core/src/index.tsx
+++ b/packages/start-client-core/src/index.tsx
@@ -1,7 +1,4 @@
-export type {
- DehydratedRouter,
- JsonResponse,
-} from '@tanstack/router-core/ssr/client'
+export type { JsonResponse } from '@tanstack/router-core/ssr/client'
export { hydrate, json, mergeHeaders } from '@tanstack/router-core/ssr/client'
diff --git a/packages/vue-router/src/ScriptOnce.tsx b/packages/vue-router/src/ScriptOnce.tsx
index 3c569d5437e..ecbec969041 100644
--- a/packages/vue-router/src/ScriptOnce.tsx
+++ b/packages/vue-router/src/ScriptOnce.tsx
@@ -16,8 +16,7 @@ export const ScriptOnce = Vue.defineComponent({
return () => (
)
}
@@ -35,7 +34,6 @@ export const ScriptOnce = Vue.defineComponent({
return (
diff --git a/packages/vue-router/src/Scripts.tsx b/packages/vue-router/src/Scripts.tsx
index 1588dea44e6..7b6df7b4ea2 100644
--- a/packages/vue-router/src/Scripts.tsx
+++ b/packages/vue-router/src/Scripts.tsx
@@ -4,10 +4,6 @@ import { useRouterState } from './useRouterState'
import { useRouter } from './useRouter'
import type { RouterManagedTag } from '@tanstack/router-core'
-// Script that sets the defer flag for Vue - must run BEFORE TSR bootstrap script
-// This prevents $_TSR.c() from removing scripts until Vue hydration is complete
-const VUE_DEFER_SCRIPT = 'self.$_TSR_DEFER=true'
-
export const Scripts = Vue.defineComponent({
name: 'Scripts',
setup() {
@@ -68,12 +64,6 @@ export const Scripts = Vue.defineComponent({
const allScripts: Array = []
if (router.serverSsr) {
- allScripts.push({
- tag: 'script',
- attrs: { nonce },
- children: VUE_DEFER_SCRIPT,
- } as RouterManagedTag)
-
const serverBufferedScript = router.serverSsr.takeBufferedScripts()
if (serverBufferedScript) {
allScripts.push(serverBufferedScript)
@@ -89,7 +79,6 @@ export const Scripts = Vue.defineComponent({
tag: 'script',
attrs: {
nonce,
- class: '$tsr',
id: '$tsr-stream-barrier',
'data-allow-mismatch': true,
},
diff --git a/packages/vue-start-client/src/StartClient.tsx b/packages/vue-start-client/src/StartClient.tsx
index 2b660b6fd6e..8a3e29505a5 100644
--- a/packages/vue-start-client/src/StartClient.tsx
+++ b/packages/vue-start-client/src/StartClient.tsx
@@ -2,15 +2,6 @@ import * as Vue from 'vue'
import { RouterProvider } from '@tanstack/vue-router'
import type { AnyRouter } from '@tanstack/router-core'
-declare global {
- interface Window {
- $_TSR?: {
- cleanup?: () => void
- [key: string]: unknown
- }
- }
-}
-
export const StartClient = Vue.defineComponent({
name: 'StartClient',
props: {
@@ -20,12 +11,12 @@ export const StartClient = Vue.defineComponent({
},
},
setup(props) {
- // After Vue hydration is complete, trigger cleanup of $tsr scripts
+ // After Vue hydration is complete, signal that router hydration is complete so cleanup can happen if stream has ended
// Use nextTick to ensure all child component onMounted hooks have completed
- // This prevents removing scripts before components have finished transitioning
+ // This prevents the cleanup to happen before components have finished transitioning
Vue.onMounted(() => {
Vue.nextTick(() => {
- window.$_TSR?.cleanup?.()
+ window.$_TSR?.h()
})
})