useEventListener
Registers an event listener with addEventListener on mount and automatically removes it with removeEventListener on unmount.
Targets can be Ref$, MaybeElement, a plain Element, Window, or Document.
The listener is always called with the latest closure value — state changes never cause stale callbacks.
Window (default target)
Section titled “Window (default target)”When no target is provided, the listener is attached to window.
import { useEventListener } from "@usels/core";
function Component() { useEventListener("keydown", (ev) => { console.log(ev.key); });
return null;}Element target
Section titled “Element target”import { useRef$, useEventListener } from "@usels/core";
function Component() { const el$ = useRef$<HTMLDivElement>();
useEventListener(el$, "click", (ev) => { console.log("clicked", ev.target); });
return <div ref={el$} />;}Reactive Ref$ target
Section titled “Reactive Ref$ target”When an Ref$ or MaybeElement is passed as the target, the listener is automatically re-registered whenever the element changes.
const el$ = useRef$<HTMLButtonElement>();
useEventListener(el$, "pointerdown", (ev) => { ev.preventDefault();});
return <button ref={el$} />;Multiple events
Section titled “Multiple events”useEventListener(el$, ["mouseenter", "mouseleave"], (ev) => { console.log(ev.type);});Multiple listeners
Section titled “Multiple listeners”useEventListener(el$, "click", [onClickA, onClickB]);Document / Window target
Section titled “Document / Window target”useEventListener(document, "visibilitychange", () => { console.log(document.visibilityState);});AddEventListenerOptions
Section titled “AddEventListenerOptions”useEventListener(el$, "scroll", onScroll, { passive: true });Manual cleanup
Section titled “Manual cleanup”The hook returns a cleanup function for imperative removal before unmount.
const stop = useEventListener("resize", onResize);
// remove the listener earlystop();Plain element targets are not reactive. If you pass a plain HTMLElement or null value and that reference changes after mount (e.g. via useState), the hook does not detect the change. Use Ref$ or MaybeElement for targets that change over time.
// ❌ listener stays on the original element if el changes via stateconst [el, setEl] = useState<HTMLDivElement | null>(null);useEventListener(el, "click", handler);
// ✅ listener is re-registered automatically when el$ changesconst el$ = useRef$<HTMLDivElement>();useEventListener(el$, "click", handler);Type Declarations
Section titled “Type Declarations”export interface GeneralEventListener<E = Event> { (evt: E): void;}export declare function useEventListener<E extends keyof WindowEventMap>(event: Arrayable<E>, listener: Arrayable<(ev: WindowEventMap[E]) => void>, options?: MaybeObservable<boolean | AddEventListenerOptions>): () => void;export declare function useEventListener<E extends keyof WindowEventMap>(target: Window, event: Arrayable<E>, listener: Arrayable<(ev: WindowEventMap[E]) => void>, options?: MaybeObservable<boolean | AddEventListenerOptions>): () => void;export declare function useEventListener<E extends keyof DocumentEventMap>(target: Document, event: Arrayable<E>, listener: Arrayable<(ev: DocumentEventMap[E]) => void>, options?: MaybeObservable<boolean | AddEventListenerOptions>): () => void;export declare function useEventListener<E extends keyof HTMLElementEventMap>(target: MaybeElement | MaybeElement[] | null | undefined, event: Arrayable<E>, listener: Arrayable<(ev: HTMLElementEventMap[E]) => void>, options?: MaybeObservable<boolean | AddEventListenerOptions>): () => void;export declare function useEventListener<EventType = Event>(target: Observable<unknown>, event: Arrayable<string>, listener: Arrayable<GeneralEventListener<EventType>>, options?: MaybeObservable<boolean | AddEventListenerOptions>): () => void;export declare function useEventListener<EventType = Event>(target: EventTarget | null | undefined, event: Arrayable<string>, listener: Arrayable<GeneralEventListener<EventType>>, options?: MaybeObservable<boolean | AddEventListenerOptions>): () => void;Source
Section titled “Source”Contributors
Section titled “Contributors”- tigerwest
Changelog
Section titled “Changelog”a7392ab2026-03-06 - feat(core,browser): add sync strategy hooks (tigerwest)