Choosing Between ko.computed and ko.pureComputed in Knockout.js
Learn when to prefer Knockout's pure computed observables over regular computeds to reduce unnecessary UI updates and improve performance.
29 Aug 2025, 10:13 UTC

The Problem: Unnecessary Recalculations Slow Down UI
When building interactive forms with Knockout.js, it is common to derive values from user inputs—such as a full name from first and last name fields. If you wrap that derivation in a regular ko.computed, the function runs every time any of its dependencies change. In a tight loop—like a user typing rapidly—this can cause the computed to re‑evaluate on each keystroke, leading to noticeable UI lag or wasted CPU cycles.
Understanding ko.computed vs ko.pureComputed
Knockout provides two flavours for derived state:
ko.computed– always creates a subscription to its dependencies and notifies its own subscribers whenever the computed function is re‑evaluated, even if the resulting value is identical to the previous one.ko.pureComputed– a special case that only notifies subscribers when the computed value actually changes. If the function’s output stays the same, Knockout skips sending change notifications, which can reduce unnecessary UI updates and subscriptions.
Both behave the same regarding dependency tracking; the difference lies in how aggressively they push updates.
Worked Example: Live Full Name with Pure Computed
The following snippet shows a simple form where the full name updates instantly as the user types. We use ko.pureComputed because we only need the derived value for display—no side effects are required.
<!-- Include Knockout from a CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-min.js"></script>
<div>
First name: <input data-bind="value: firstName" /><br/>
Last name: <input data-bind="value: lastName" /><br/>
Full name: <span data-bind="text: fullName"></span>
</div>
<script>
function AppViewModel() {
this.firstName = ko.observable('');
this.lastName = ko.observable('');
// Pure computed – only updates when the concatenated string changes
this.fullName = ko.pureComputed(function () {
return (this.firstName() + ' ' + this.lastName()).trim();
}, this);
}
ko.applyBindings(new AppViewModel());
</script>
Open the browser console and add a temporary subscription to see how often the computed runs:
var vm = new AppViewModel();
vm.fullName.subscribe(function(newVal) {
console.log('fullName changed to:', newVal);
});
ko.applyBindings(vm);
// Try typing in the inputs – you’ll see the log only when the actual value shifts.
Trade‑off: When You Need Side Effects
If your derived logic must perform an action—such as logging, making an Ajax request, or updating another observable—you cannot rely on ko.pureComputed because it may skip execution when the value does not change. In those cases, revert to ko.computed and consider throttling or rate‑limiting the evaluation to avoid excess work.
this.searchTerm = ko.observable('');
this.results = ko.observableArray([]);
this.searchRequest = ko.computed(function () {
var term = this.searchTerm();
if (term.length >= 3) {
// Simulate an async call – in real code you’d use ajax
console.log('Fetching results for:', term);
// this.results([...]);
}
}, this).extend({ rateLimit: 300 }); // wait 300ms after last change
The rateLimit extender ensures the Ajax‑like work runs at most twice per second, keeping the UI responsive.
Verification and Cleanup
To confirm that a pure computed behaves as expected:
- Load the example page in a browser.
- Open the developer tools console.
- Enter
vm.fullName.peek()to read the current value without triggering a subscription. - Change one input and observe the console log from the subscription—it fires only when the displayed full name actually changes.
- Dispose of any manually created subscriptions in a
disposemethod if you embed the viewmodel in a larger application to prevent memory leaks.
Actionable Takeaway
Use ko.pureComputed for derived values that are purely functional and consumed by the UI. Switch to ko.computed (with throttling or debouncing when needed) when you require side effects or must guarantee execution on every dependency change. Always verify the behavior with a console subscription and clean up subscriptions to avoid leaks in long‑running apps.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.