Skip to content

<Transition mode="out-in"> + <Suspense> inside a <template v-if> fragment leaks DOM on toggle #14761

@Flo0806

Description

@Flo0806

Vue version

3.5.30

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-assboxgb

Steps to reproduce

Hi Vue team,

when toggling between two <template v-if> branches that each contain a <Transition mode="out-in"> wrapping a <Suspense> with an async-setup component, every toggle leaves the previous DOM subtree behind in the container.After N clicks you end up with N+1 copies of the async child rendered. Sibling nodes in the same Fragment (e.g. an <h4>) are removed correctly - only the Transition/Suspense subtree leaks.

I came across this from (as a Member) nuxt/nuxt#34859 - it surfaces there because renders exactly this structure internally. I reduced it to a Vue-only repro (no Nuxt, no vue-router), which is why I'm filing it here. Conditions (all required to reproduce):

  • Conditional parent is a <template v-if> Fragment - switching to <div v-if> makes it disappear
  • <Transition mode="out-in"> - removing mode makes it disappear
  • A <Suspense> inside the Transition
  • A descendant component with async setup (so Suspense actually suspends)

What I observed while debugging: the component lifecycle looks clean - onUnmounted fires for every leaving instance. But the DOM subtree stays attached to the container anyway. This points at the out-in leave path interacting badly with suspensible Suspense: the afterLeave callback that should call hostRemove(el) never runs, because the surrounding itself is torn down before it gets a chance to fire.

What is expected?

Component should been removed from DOM.

What is actually happening?

Component is still staying with N copies.

System Info

Any additional comments?

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    🔨 p3-minor-bugPriority 3: this fixes a bug, but is an edge case that only affects very specific usage.has workaroundA workaround has been found to avoid the problemscope: suspense

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions