Skip to content

bug(svelte): useAskable.svelte.ts registers event listeners outside lifecycle — no cleanup #252

Closed as not planned

Description

@vamgan

Summary

In the Svelte 5 (runes) variant of useAskable, focus and clear event listeners are registered synchronously during module initialization — not inside a lifecycle hook. There is no corresponding cleanup, so if the component unmounts, listeners remain on the context indefinitely.

Root cause

packages/svelte/src/useAskable.svelte.ts:

let focus: AskableFocus | null = $state(null);

ctx.on('focus', (f) => { focus = f; });   // registered at init, never removed
ctx.on('clear', () => { focus = null; }); // registered at init, never removed

if (options?.observe !== false) {
  ctx.observe(document, observeOpts);
}

Unlike the store-based Svelte implementation which returns a destroy() function, the runes variant has no teardown path at all.

Expected behavior

Listeners should be registered in an effect with proper cleanup, or the function should return a destroy() handle the caller is responsible for calling.

Fix

Use $effect for registration and cleanup:

$effect(() => {
  const handleFocus = (f: AskableFocus) => { focus = f; };
  const handleClear = () => { focus = null; };
  ctx.on('focus', handleFocus);
  ctx.on('clear', handleClear);
  return () => {
    ctx.off('focus', handleFocus);
    ctx.off('clear', handleClear);
  };
});

Activity

  1. added a commit that references this issue on Jun 1, 2026
    b4beb20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: svelteSvelte adapter workbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions