Computed Properties vs Event Listeners
Computed properties and event listeners both react to change, but only one models derived state honestly. Computed properties win for anything that is a function of state; event listeners win for genuine side effects.
The short answer
Computed Properties over Event Listeners for most cases. Most "listen for the change and update something" code is really derived state in disguise.
- Pick Computed Properties if the value you need is a pure function of existing state — totals, filtered lists, formatted strings, validation flags. Declare it once and stop thinking about it
- Pick Event Listeners if need a real side effect on a discrete moment: a click, a network response, a DOM event, analytics, focus management. There is nothing to derive — something must HAPPEN
- Also consider: They are not rivals. A clean app is mostly computed values with a thin shell of listeners at the edges where the outside world pokes in. The bug factory is using listeners to fake derived state.
— Nice Pick, opinionated tool recommendations
What each one actually is
A computed property is a value defined as a function of other values. You declare what it depends on (or the framework tracks it), and it recomputes when those inputs change — cached until then. It is pull-based: ask for it, get the current answer. An event listener is a callback you register against a source — a DOM node, an emitter, a socket. When the event fires, your function runs, and whatever it does is up to you, including mutating five unrelated things. It is push-based and imperative. The category error people make is treating these as interchangeable because both 'respond to change.' They don't respond to the same thing. Computed properties respond to STATE. Listeners respond to EVENTS. State changes are continuous and declarative; events are discrete and procedural. Pick by which one your problem actually is, not by which API you typed first.
Where computed properties earn their keep
Anytime a value is determined by other values, a computed property is correct and a listener is a liability. Cart total from line items. Full name from first and last. Filtered, sorted view of a list. 'Is this form valid' from the fields. With a computed property you write the formula once; the framework recomputes on dependency change and caches between reads so a getter referenced ten times in a render costs one evaluation. You cannot forget to update it, because there is no update — there's only the formula. Do the same thing with listeners and you hand-wire every input to a handler that recalculates and writes the result somewhere. Miss one input and the total silently lies. Add an input later and you must remember to wire it. Computed properties make the dependency graph the framework's problem. That's the whole point, and it's why reactive frameworks lead with them.
Where event listeners are non-negotiable
Listeners own the boundary between your state and everything outside it. A click is not derivable. A keystroke, a drag, a websocket message, a scroll position, a 'payment succeeded' webhook — these are events from the world, and you cannot compute them into existence. You must register a callback and react. This is also where side effects belong: firing analytics, starting a fetch, playing a sound, moving focus, writing to localStorage. Trying to cram side effects into a computed property is the inverse sin — computed getters must be pure, and a getter that POSTs to your API will run at absurd times, twice, or never, depending on caching and access patterns. Listeners are honest about the fact that something irreversible happens. The skill is keeping them THIN: a listener should capture the event, update state, and get out — then let computed properties derive everything downstream.
The architecture that actually scales
Stop choosing. The maintainable shape is a small ring of event listeners around a large core of computed values. Listeners sit at the edges, translate raw world-events into state mutations, and do nothing clever. Everything that is a function of that state — the UI, the totals, the flags, the derived views — is computed, declared once, recomputed automatically. The codebases that rot are the ones that invert this: fat listeners that recompute derived state by hand, scatter writes across the app, and drift out of sync the moment two listeners touch the same value in the wrong order. That's where 'why is the badge count wrong' bugs live. So the rule, stated meanly: if you find yourself in an event handler recalculating something you could have declared as a formula, you reached for the wrong tool and you're about to debug it at 2am. Derive by default. Listen only at the boundary.
Quick Comparison
| Factor | Computed Properties | Event Listeners |
|---|---|---|
| Best for | Values derived from existing state (totals, filters, flags) | Discrete world-events and side effects (clicks, sockets, analytics) |
| Sync correctness | Auto-tracked dependencies; cannot drift out of date | Manual wiring; forget an input and state silently lies |
| Performance | Cached between reads; recompute only on dependency change | Runs every fire; cheap per call but easy to over-trigger |
| Side effects | Must stay pure — effects here misfire or never run | The correct and only home for impure work |
| Maintenance at scale | Declarative formula, self-documenting dependency graph | Imperative, scatters writes, ordering bugs across handlers |
The Verdict
Use Computed Properties if: The value you need is a pure function of existing state — totals, filtered lists, formatted strings, validation flags. Declare it once and stop thinking about it.
Use Event Listeners if: You need a real side effect on a discrete moment: a click, a network response, a DOM event, analytics, focus management. There is nothing to derive — something must HAPPEN.
Consider: They are not rivals. A clean app is mostly computed values with a thin shell of listeners at the edges where the outside world pokes in. The bug factory is using listeners to fake derived state.
Most "listen for the change and update something" code is really derived state in disguise. Computed properties declare the dependency, recompute automatically, cache the result, and never drift out of sync. Event listeners make you do all of that by hand, badly.
Related Comparisons
Disagree? nice@nicepick.dev