Derive UI State with Knockout Computed Observables
Use Knockout computed observables to derive values like task counts from model observables, so the UI updates automatically without manual DOM manipulation.
22 Mar 2026, 09:59 UTC

The problem: keeping derived UI in sync
Imagine a todo list where each task has an observable completion flag. You also want a badge showing how many tasks remain incomplete. Without a reactive dependency, you must remember to update that badge every time a task is added, removed, or toggled. Miss one code path, and the number shown in the browser no longer matches the underlying model.
Knockout solves this with computed observables: functions that read other observables and automatically re-run when any of those dependencies change. The result is a value you can bind directly to the DOM, so the UI updates without manual event handling.
How computed observables track dependencies
An observable is an object that holds a value and notifies subscribers when that value changes. A computed observable wraps an evaluator function. When Knockout first evaluates that function, it records every observable the function touches. Later, if any recorded observable changes, Knockout re-evaluates the function and pushes the new value to every binding that uses it.
This means your view-model stays declarative. You describe what the count means, not when to recalculate it.
Worked example: incomplete task count
Here is a small view-model with an observable array of tasks and a computed observable for the incomplete count.
function TaskViewModel() {
this.tasks = ko.observableArray([
{ name: "Review pull request", isDone: ko.observable(false) },
{ name: "Update documentation", isDone: ko.observable(true) },
{ name: "Deploy to staging", isDone: ko.observable(false) }
]);
this.incompleteCount = ko.computed(function () {
var count = 0;
this.tasks().forEach(function (task) {
if (!task.isDone()) {
count += 1;
}
});
return count;
}, this);
}
ko.applyBindings(new TaskViewModel());
The corresponding markup binds the computed to a span:
<span data-bind="text: incompleteCount"></span> task(s) remaining
Because incompleteCount reads task.isDone() inside its evaluator, Knockout tracks each task's done flag as a dependency. Toggling any checkbox re-runs the computed and updates the bound span automatically.
Trade-offs: pureComputed, cost, and side effects
Computed observables are not free. Every dependency change triggers a full re-evaluation of the evaluator function. If that function performs heavy filtering or sorting over a large array, the UI can lag. For expensive derivations that are not currently displayed, consider pureComputed, which skips re-evaluation when nothing is subscribed to the result.
Avoid asynchronous calls, timers, or non-observable reads inside the evaluator. Knockout cannot track those as dependencies, so the computed may return stale values or fail to update when expected.
Verify dependency tracking in the browser
- Open the page in a browser and open the developer tools console.
- Temporarily add
console.log("re-evaluating")as the first line inside the computed's evaluator function. - Toggle a task's completion flag in the UI, or run
viewModel.tasks()[0].isDone(true)in the console, replacingviewModelwith your actual view-model instance. - Check that the log appears and the bound span updates to the expected incomplete count.
- Remove the temporary log before committing.
If the log does not appear after changing an observable, the computed is likely reading a plain JavaScript property instead of calling an observable as a function.
Closing
Use computed observables for values derived from other observables, such as totals, filtered lists, or button enabled flags. Keep the evaluator synchronous and side-effect free, and verify in the console that changes to dependencies trigger re-evaluation. This keeps your UI consistent with your model without manual DOM wiring.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.