svelte-dom-leak-repro Svelte Themes

Svelte Dom Leak Repro

Svelte 5 — detached DOM heap repro

Minimal SvelteKit app in SPA mode (ssr = false, @sveltejs/adapter-static + fallback: 'index.html') to reproduce accumulation of detached DOM nodes after mount/destroy cycles when the compiled template holds multiple DOM references (wrapper + <video> + controls) in shared closure scopes.

Upstream issue: sveltejs/svelte#18096

Run

npm install
npm run dev

Production SPA build (index.html fallback):

npm run build
npm run preview

Reproduction (Chrome DevTools)

  1. Open MemoryHeap snapshot → snapshot A (baseline).
  2. Interact with the grid: click counter and play/pause buttons on several different rows (not only the page-level stress button). This matters: without row clicks, detached counts may stay low.
  3. Click “Stress 600 remounts” and wait until it completes. Each iteration destroys a 48×(video + bind: + buttons) grid and mounts a new one.
  4. Optionally run gc() in the console (Chrome: --js-flags="--expose-gc").
  5. Take snapshot B. In B, filter Detached and compare counts for HTMLVideoElement, HTMLDivElement (class row), etc.

You can also use “Remount ×40” several times before step 5.

A single tiny component often shows no visible delta between snapshots because GC clears between captures; the grid + row clicks + stress sequence is what made the issue observable for us.

Chrome documents the underlying DOM retention pattern: a JS reference to any node in a detached subtree keeps the entire subtree alive via parentNode / child links until that reference is dropped.

What we tested upstream

Clearing video.src / load() from app onDestroy did not fix large detached counts in a real app; extending remove_effect_dom in the Svelte runtime (clear event_symbol, reset media, recursively remove children after remove()) did (~99% reduction in detached delta in that scenario). See svelte#18096 for the reference patch.

Top categories

Loading Svelte Themes