Skip to content

fix(teleport): avoid requeue loop during suspense branch swap#14642

Open
edison1105 wants to merge 2 commits intomainfrom
edison/fix/14640
Open

fix(teleport): avoid requeue loop during suspense branch swap#14642
edison1105 wants to merge 2 commits intomainfrom
edison/fix/14640

Conversation

@edison1105
Copy link
Member

@edison1105 edison1105 commented Mar 27, 2026

close #14640

Summary by CodeRabbit

  • Bug Fixes
    • Teleport now defers and removes teleported content correctly during updates—avoiding unexpected removal or premature mounting when deferment or a parent suspense pending state is involved.
  • Tests
    • Added a Suspense test verifying teleported content remains absent or present as expected across async resolution and toggling.

@coderabbitai
Copy link

coderabbitai bot commented Mar 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b8dfb60e-ac79-4405-a19a-8263513be1be

📥 Commits

Reviewing files that changed from the base of the PR and between e06a4da and 2492738.

📒 Files selected for processing (2)
  • packages/runtime-core/__tests__/components/Suspense.spec.ts
  • packages/runtime-core/src/components/Teleport.ts

📝 Walkthrough

Walkthrough

Teleport's update/remove logic was changed to postpone patching during updates only when the teleport's target element is still unmounted and the teleport is explicitly deferred or resides in a parent Suspense with a pendingBranch. remove now tracks a pending mount flag to avoid removing teleported children when a previously-cleared mount effect would have caused deferral.

Changes

Cohort / File(s) Summary
Teleport runtime changes
packages/runtime-core/src/components/Teleport.ts
Adjusts process to defer teleport patching during updates only if target is unmounted AND (teleport is explicitly deferred OR parent Suspense has pendingBranch). In remove it tracks isPendingMount and prevents removing teleported children when removal should be suppressed due to a pending mount state.
Suspense test coverage
packages/runtime-core/__tests__/components/Suspense.spec.ts
Adds a Suspense test that conditionally renders a Teleport in the default slot to assert teleported content remains unmounted when the teleport is toggled off before async resolution and ensures no teleport markers/children are mounted after resolution.

Sequence Diagram(s)

sequenceDiagram
  participant App as "App / Component"
  participant Suspense as "Suspense"
  participant Teleport as "TeleportImpl"
  participant Scheduler as "Scheduler / postFlush"
  participant Target as "Teleport Target (DOM)"

  rect rgba(0,128,0,0.5)
  App->>Suspense: render (async branch + teleport in default)
  end

  rect rgba(0,0,255,0.5)
  Suspense->>Teleport: patch/update vnode
  Teleport->>Teleport: check target.__isMounted
  Teleport->>Teleport: check isTeleportDeferred(props) OR parentSuspense.pendingBranch
  alt should defer
    Teleport->>Scheduler: queuePostFlushCb (defer patch)
  else immediate
    Teleport->>Target: mount/patch children
  end
  end

  rect rgba(128,0,128,0.5)
  Scheduler->>Teleport: run deferred patch (post-flush)
  Teleport->>Target: mount/patch children (if still applicable)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

scope: teleport, scope: suspense, :hammer: p3-minor-bug

Poem

🐰 I hopped through Suspense and found a teleport stall,
A mount left pending, awaiting the call.
I nudged the checks, deferred with a wink,
Now children wait safe — no scheduler sink.
Hooray — pages move on, no more dizzying fall! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly describes the primary fix: avoiding a requeue loop during Suspense branch swaps in Teleport behavior, which directly addresses the linked issue.
Linked Issues check ✅ Passed Code changes directly address the regression: updated Teleport.process defers patching only when target is unmounted and conditions warrant; Teleport.remove now tracks pending mounts and prevents child removal when only pending effects are deferred, eliminating the infinite loop.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing Teleport behavior during Suspense interactions. Implementation in Teleport.ts handles deferred patching and mount tracking; test adds validation that teleported content is not mounted when Suspense branch is discarded.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch edison/fix/14640

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 27, 2026

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@14642
npm i https://pkg.pr.new/@vue/compiler-core@14642
yarn add https://pkg.pr.new/@vue/compiler-core@14642.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@14642
npm i https://pkg.pr.new/@vue/compiler-dom@14642
yarn add https://pkg.pr.new/@vue/compiler-dom@14642.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@14642
npm i https://pkg.pr.new/@vue/compiler-sfc@14642
yarn add https://pkg.pr.new/@vue/compiler-sfc@14642.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@14642
npm i https://pkg.pr.new/@vue/compiler-ssr@14642
yarn add https://pkg.pr.new/@vue/compiler-ssr@14642.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@14642
npm i https://pkg.pr.new/@vue/reactivity@14642
yarn add https://pkg.pr.new/@vue/reactivity@14642.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@14642
npm i https://pkg.pr.new/@vue/runtime-core@14642
yarn add https://pkg.pr.new/@vue/runtime-core@14642.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@14642
npm i https://pkg.pr.new/@vue/runtime-dom@14642
yarn add https://pkg.pr.new/@vue/runtime-dom@14642.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@14642
npm i https://pkg.pr.new/@vue/server-renderer@14642
yarn add https://pkg.pr.new/@vue/server-renderer@14642.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@14642
npm i https://pkg.pr.new/@vue/shared@14642
yarn add https://pkg.pr.new/@vue/shared@14642.tgz

vue

pnpm add https://pkg.pr.new/vue@14642
npm i https://pkg.pr.new/vue@14642
yarn add https://pkg.pr.new/vue@14642.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@14642
npm i https://pkg.pr.new/@vue/compat@14642
yarn add https://pkg.pr.new/@vue/compat@14642.tgz

commit: 2492738

@github-actions
Copy link

github-actions bot commented Mar 27, 2026

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 105 kB (+110 B) 39.8 kB (+32 B) 35.8 kB (-6 B)
vue.global.prod.js 164 kB (+110 B) 59.8 kB (+31 B) 53.2 kB (-67 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 48.3 kB 18.8 kB 17.2 kB
createApp 56.5 kB 21.8 kB 19.9 kB
createSSRApp 60.7 kB 23.6 kB 21.5 kB
defineCustomElement 62.6 kB 23.8 kB 21.7 kB
overall 71 kB 27.2 kB 24.7 kB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Teleport + Suspense unmount crash during page navigation in Nuxt SSR (regression in 3.5.31)

1 participant