Vue version
3.5.31
Link to minimal reproduction
https://github.com/Gabriel-SPassos/vue-teleport-regression
Steps to reproduce
git clone https://github.com/Gabriel-SPassos/vue-teleport-regression
cd vue-teleport-regression
npm install
npm run dev
- Open http://localhost:3000
- Click "Go"
- Check the browser console
What is expected?
Navigation to Page B completes without errors. Teleported elements from the previous page are cleanly unmounted.
What is actually happening?
Dev mode (npm run dev):
RangeError: Maximum call stack size exceeded
at queuePostFlushCb
at queueEffectWithSuspense
at Object.process
at flushPostFlushCbs
at flushJobs
Infinite recursion in the scheduler — deferred Teleport post-flush callbacks create a loop between queueEffectWithSuspense and flushJobs.
Production mode — observed in a real-world Nuxt app:
TypeError: Cannot destructure property 'bum' of 'y' as it is null.
at unmountComponent
at unmount
at Object.remove (Teleport.remove)
The page freezes — URL updates but the UI does not re-render.
Vue 3.5.30: No errors in either dev or production mode. Navigation works as expected.
System Info
System:
OS: macOS 15.5
Node: v20.19.0
Binaries:
npm: 10.8.2
npmPackages:
vue: 3.5.31
nuxt: ^4.1.1
Any additional comments?
Scenario:
- Nuxt 4 app with SSR enabled
- Page A (
index.vue): await useFetch() in setup (triggers Suspense wrapping by <NuxtPage>), two <teleport to="body"> blocks each containing a <Transition> wrapping v-if content with nested child components, reactive state changes after navigateTo() (clearing items array + resetting a flag in finally)
- Page B (
page-b.vue): has async setup (await useFetch(...)) and its own <teleport to="body">
- The crash occurs during the Suspense branch swap — deferred Teleport post-flush callbacks create an infinite loop in the scheduler
Candidate PRs merged between 3.5.30 and 3.5.31:
| PR |
Description |
Suspicion |
| #9392 |
fix(suspense): avoid unmount activeBranch twice if wrapped in transition |
High |
| #8619 |
fix(runtime-dom): defer teleport mount/update until suspense resolves |
High |
| #12922 |
fix(suspense): update suspense vnode's el during branch self-update |
Medium |
The interaction between #8619 (deferring Teleport operations when parentSuspense.pendingBranch exists) and #9392 (changing Suspense unmount timing when wrapped in Transition) appears to create this issue during Nuxt's page navigation cycle (Suspense + Transition + Teleport).
Workaround: Pin vue to 3.5.30:
{
"overrides": {
"vue": "3.5.30"
}
}
Vue version
3.5.31
Link to minimal reproduction
https://github.com/Gabriel-SPassos/vue-teleport-regression
Steps to reproduce
git clone https://github.com/Gabriel-SPassos/vue-teleport-regression cd vue-teleport-regression npm install npm run devWhat is expected?
Navigation to Page B completes without errors. Teleported elements from the previous page are cleanly unmounted.
What is actually happening?
Dev mode (
npm run dev):Infinite recursion in the scheduler — deferred Teleport post-flush callbacks create a loop between
queueEffectWithSuspenseandflushJobs.Production mode — observed in a real-world Nuxt app:
The page freezes — URL updates but the UI does not re-render.
Vue 3.5.30: No errors in either dev or production mode. Navigation works as expected.
System Info
Any additional comments?
Scenario:
index.vue):await useFetch()in setup (triggers Suspense wrapping by<NuxtPage>), two<teleport to="body">blocks each containing a<Transition>wrappingv-ifcontent with nested child components, reactive state changes afternavigateTo()(clearing items array + resetting a flag infinally)page-b.vue): has async setup (await useFetch(...)) and its own<teleport to="body">Candidate PRs merged between 3.5.30 and 3.5.31:
The interaction between #8619 (deferring Teleport operations when
parentSuspense.pendingBranchexists) and #9392 (changing Suspense unmount timing when wrapped in Transition) appears to create this issue during Nuxt's page navigation cycle (Suspense + Transition + Teleport).Workaround: Pin
vueto3.5.30:{ "overrides": { "vue": "3.5.30" } }