Hooks

useEventListener

Attach event listeners to any target - window, document, or an element - with automatic cleanup on teardown. The target may be a reactive getter so the listener re-binds as the target changes. SSR-safe.

Window target

Target window with a getter () => window so it stays SSR-safe - the target is only read in the browser. The listener is removed automatically when the component unmounts.

Last key: (press any key)

<script lang="ts">
  import { useEventListener } from 'sv5ui';

  let keyPressed = $state('');

  // Use a getter (() => window) so it stays SSR-safe -
  // the target is only read in the browser, inside an effect.
  useEventListener(() => window, 'keydown', (e) => {
    keyPressed = e.key;
  });
</script>

<p>Last key: <strong>{keyPressed || '(press any key)'}</strong></p>

Reactive element target

Pass a getter () => el when the element is bound with bind:this. The hook re-attaches whenever the getter returns a new element.

Hover over me
<script lang="ts">
  import { useEventListener } from 'sv5ui';

  let el = $state<HTMLDivElement>();
  let hovered = $state(false);

  useEventListener(() => el, 'pointerenter', () => (hovered = true));
  useEventListener(() => el, 'pointerleave', () => (hovered = false));
</script>

<div bind:this={el} class="rounded border p-4">
  {hovered ? 'Hovering!' : 'Hover over me'}
</div>

Multiple event types

Pass an array of event names to attach the same handler to all of them in one call.

<script lang="ts">
  import { useEventListener } from 'sv5ui';

  let active = $state(false);
  let el = $state<HTMLButtonElement>();

  // Listen to multiple events in one call
  useEventListener(() => el, ['pointerenter', 'focus'], () => (active = true));
  useEventListener(() => el, ['pointerleave', 'blur'], () => (active = false));
</script>

<button bind:this={el} class="rounded border px-3 py-1.5">
  {active ? 'Active' : 'Hover or focus me'}
</button>

Listener options

The fourth argument is passed directly to addEventListener. Use passive: true for scroll/touch handlers, once: true to fire once, or capture: true for capture phase.

See code - no visible output for this snippet.

<script lang="ts">
  import { useEventListener } from 'sv5ui';

  let clicks = $state(0);

  // { once: true } - fires once then removes itself
  useEventListener(() => window, 'click', () => clicks++, { once: false });

  // { passive: true } - never calls preventDefault
  useEventListener(() => document, 'scroll', () => console.log('scrolled'), { passive: true });
</script>

Parameters

ParameterTypeDefault
targetWindow | Document | HTMLElement | EventTarget | (() => …) | null-
typestring | string[]-
handler(event) => void-
optionsboolean | AddEventListenerOptionsundefined