use:action Directive: DOM Element Lifecycle Attachment
- Last updated
- Prerequisites:
- Svelte element directives
- Understanding of DOM lifecycle and cleanup
- svelte
- actions
- use-directive
- lifecycle
- dom
- directives
Read at your depth
The practical view
Actions run a function when an element is created: <div use:tooltip>Hover me</div>. The action receives the element (and an optional parameter): function tooltip(node, params) { ... return { update(params) {}, destroy() {} }; }. The action runs once on mount, update() runs when the parameter changes, and destroy() runs when the element is removed. Use actions for: focus management, click-outside handling, IntersectionObserver attachment, drag-and-drop, and any imperative DOM work that has a lifecycle. They are Svelte's replacement for the imperative parts of old Angular/React directives.
The same idea in other frameworks
React custom hooks + refs approximate actions, but with manual lifecycle management.Angular directives with ngOnInit/ngOnChanges/ngOnDestroy are the direct analogues.Vue custom directives (mounted/updated/unmounted) mirror the action lifecycle.Legacy vs modern
Manual addEventListener + cleanup vs use:action
Attaching listeners in onMount requires manual removal in onDestroy and manual re-wiring on param change; the action lifecycle handles all three phases.
onMount(() => {
el.addEventListener('click', handler); // ❌ OLD
return () => el.removeEventListener('click', handler); // ❌ OLD: manual
});function tooltip(node, params) {
function onEnter() { params.show = true; }
// ✅ NEW: attach in the action, clean up in destroy()
node.addEventListener('mouseenter', onEnter);
return { update(p) {}, destroy() { node.removeEventListener('mouseenter', onEnter); } };
}Interview gotchas
Context
This tests the three-phase contract — interviewers want to confirm you know the action runs on mount, update() reacts to param changes, and destroy() runs on unmount, and how the reactive param tracking works.
The mechanical answer
The action function runs once when the element is inserted into the DOM, receiving the node and the current parameter value. If it returns an object with update(), Svelte calls update(newParams) whenever the action's parameter expression changes — the compiler wraps the update call in an effect that tracks any $state read inside the parameter expression, so reactive params trigger updates automatically. destroy() is called when the element is removed (during block teardown), letting the action release listeners/observers. The action does not auto-re-run its main body on param changes; only update() does. This contract makes actions safe for third-party integrations: setup in the body, reaction in update, teardown in destroy.
Trap
The naive answer is 'the action function re-runs when params change'. The body runs once; update() is the reactive phase. Another trap: forgetting destroy() — actions that attach listeners without cleanup leak them when the element unmounts, exactly the bug actions are meant to prevent. Candidates who enumerate the three phases and the effect-wrapped update mechanism show real understanding.
Context
This probes the design boundaries — interviewers want to know why actions exist as a distinct primitive rather than onMount + bind:this boilerplate, and when each is appropriate.
The mechanical answer
Event handlers (onclick) are declarative for simple events directly on the element. onMount + bind:this works for imperative setup but requires manual cleanup (return a teardown or use onDestroy) and manual re-wiring when parameters change. Actions formalize the imperative lifecycle: attach (body), react to param changes (update), and clean up (destroy) in one reusable unit that can be applied to any element with use:. They are the right choice when the behavior is reusable (tooltips, observers, drag handlers, library mount points) and needs parameterized reactivity and guaranteed teardown. For one-off component-internal imperative setup, onMount remains fine; for reusable, parameterized element behaviors, actions are idiomatic.
Trap
The naive answer is 'actions are just event handlers'. Event handlers don't provide lifecycle or parameter updates. Another trap: assuming actions run during SSR — they are client-only (the node doesn't exist server-side), so SSR markup omits action effects and they activate on hydration. Candidates who describe the reusability and the SSR/client boundary show real understanding.