Choosing ko.pureComputed for Derived UI State in Knockout.js
A practical guide to picking ko.pureComputed for derived values like cart totals, with a worked example and trade‑offs.
14 Dec 2025, 05:05 UTC

The problem: derived values that leak or run too often
Knockout developers often reach for ko.computed whenever a value depends on other observables. In short‑lived components — think modal dialogs, tab panels, or list items that are added and removed — a regular computed stays alive until you manually dispose it. Forgetting that disposal creates memory leaks and forces the evaluator to run even when the UI is hidden.
Thesis: use ko.pureComputed for pure, read‑only derivations
Starting with Knockout 3.2, ko.pureComputed gives you a computed that evaluates only while something subscribes to it. When the last subscriber disappears, the computed disposes itself automatically. This matches the typical lifecycle of derived UI state such as totals, filtered lists, or formatted strings.
How ko.pureComputed differs from ko.computed
| Behavior | ko.computed | ko.pureComputed |
|---|---|---|
| Immediate evaluation | Yes, on creation | No, waits for first subscription |
| Automatic disposal | No (manual dispose() required) | Yes, when subscriber count drops to zero |
| Side‑effects allowed | Yes (but discouraged) | No — evaluator must be pure |
| Typical use case | Any computed, especially with side effects | Derived read‑only values |
When to reach for ko.pureComputed
- The evaluator only reads observables and returns a new value (no AJAX, logging, or writes).
- The derived value is needed only while a UI element is bound — e.g., a cart total shown in a header that disappears when the cart is empty.
- You want to avoid manual
dispose()calls in components that are created and destroyed frequently.
Worked example: shopping‑cart line‑item total
Assume each line item has price and quantity observables. The line‑total is a pure derivation.
function LineItem(data) {
this.price = ko.observable(data.price);
this.quantity = ko.observable(data.quantity);
// pureComputed: no side effects, auto‑disposes when UI unbinds
this.lineTotal = ko.pureComputed(function () {
return this.price() * this.quantity();
}, this);
}
function CartViewModel() {
this.items = ko.observableArray([
new LineItem({ price: 10, quantity: 2 }),
new LineItem({ price: 5, quantity: 3 })
]);
// Cart total also pure — only evaluated while something (e.g., a span) subscribes
this.cartTotal = ko.pureComputed(function () {
var total = 0;
ko.utils.arrayForEach(this.items(), function (item) {
total += item.lineTotal();
});
return total;
}, this);
}
ko.applyBindings(new CartViewModel());
In the markup, a simple <span data-bind="text: cartTotal"></span> creates a subscription. When the cart section is removed from the DOM (e.g., via if binding), the subscription disappears and both cartTotal and each lineTotal are disposed automatically.
Trade‑offs and limitations
- No side effects: If you need to trigger an API call when the total changes, use a regular
ko.computedor a manual subscription on the pure computed. - Asynchronous reads break dependency tracking: Reading an observable inside a
setTimeoutor promise callback won’t be captured; the computed won’t re‑evaluate when that observable changes. - Version gate:
ko.pureComputedexists only from Knockout 3.2 onward. Verify withko.versionorpackage.jsonbefore relying on auto‑disposal. - Retained subscriptions keep it alive: If you manually hold a subscription (e.g.,
var sub = vm.cartTotal.subscribe(...)) and forget to dispose it, the pure computed stays in memory.
Quick verification checklist
- Confirm Knockout version ≥ 3.2:
console.log(ko.version);(run in browser console). - Create a pureComputed, subscribe, mutate a dependency, and observe the evaluator runs (add a
console.loginside the evaluator temporarily). - Dispose the subscriber (e.g., remove the binding or call
sub.dispose()) and mutate the dependency again — the evaluator should not run. - Scan the evaluator for side effects (fetch,
console.log, writes to other observables). Refactor any found into a separate subscription or regular computed.
Actionable next step
Audit your view‑models for ko.computed instances that only calculate a value from other observables. Replace each with ko.pureComputed and delete any manual dispose() calls. Run the verification checklist above to confirm automatic disposal works in your component lifecycle. This reduces leak risk and eliminates unnecessary evaluations in hidden UI sections.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.