Using Knockout.js pureComputed to Reduce Unnecessary UI Updates
Learn how Knockout.js pureComputed avoids unnecessary subscriptions and evaluations, with a concrete example showing delayed evaluation of a list length.
08 Dec 2025, 16:45 UTC

The problem: wasted work in a busy UI
Imagine a view model that holds a large observable array of items. Every time the array changes—whether you add, remove, or reorder an item—you need to show the current count in the UI. A naïve approach binds the count to a regular computed observable that reads the array length. Each push triggers the computed’s evaluator, updates its value, and notifies any subscribers, even if no part of the UI is currently reading the count. In a complex page with many such derived values, this can cause extra CPU work and memory overhead.
What pureComputed changes
Knockout’s pureComputed is a read‑only computed observable that delays two things:
- Subscription creation: it does not register a subscription to its dependencies until at least one subscriber asks for its value.
- Evaluation: when dependencies change, the evaluator is not run immediately; it runs lazily the next time the computed’s value is accessed.
If nothing is bound to the pureComputed, its evaluator never runs and it holds no subscriptions, saving both CPU cycles and memory.
Worked example: tracking list length
The following view model shows a typical pattern. An observable array items holds data; a pureComputed itemCount returns its length. We log inside the evaluator to see when it runs.
function ViewModel() {
this.items = ko.observableArray([]);
// pureComputed that reads the array length
this.itemCount = ko.pureComputed(function () {
console.log('itemCount evaluator ran');
return this.items().length;
}, this);
}
var vm = new ViewModel();
ko.applyBindings(vm);
// Simulate rapid changes
for (var i = 0; i < 5; i++) {
vm.items.push({ id: i });
}
// Now read the value (e.g., via a binding or ko.toJS)
console.log('Current count:', vm.itemCount());
When you run this snippet in a browser console, you will see the evaluator log appear once—after the final vm.itemCount() call—despite five pushes to the array. If you remove the final read, the evaluator never logs, proving that no subscription or evaluation occurred while the array was mutating.
Trade‑off and limitation
The main limitation is that pureComputed cannot be written to. Attempting to assign a value (e.g., vm.itemCount(10)) throws an error, so it is unsuitable for two‑way bindings. Additionally, because the evaluator may never run if there are no subscribers, any side‑effects placed inside the function (like logging or updating other state) will be silently skipped, which can hide bugs if you rely on those effects.
Actionable closing
Use pureComputed for any derived value that is only read by the UI—such as filtered list lengths, formatted strings, or boolean flags. Combine it with the throttle extender when the underlying observables change at high frequency (mouse moves, resize events) to further limit evaluations. Remember to keep the evaluator pure (no side‑effects) and to verify that the value is actually accessed somewhere in your bindings or manual calls; otherwise the computation will remain dormant, which is exactly the performance benefit you’re after.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.