Knockback turns a Backbone model into a Knockout view model
Knockback.js provides Knockout.js magic for Backbone.js Models and Collections.
At a glance
- What is it?
- Knockback is a TypeScript library that wraps a Backbone model in a Knockout view model, so data-bound inputs write straight back to the model and computed properties can be added on top. Its 2.0 release renamed every option from snake_case to camelCase, and the package now ships under the @mcpeasy scope.
- Who is it for?
- Adopt Knockback if you already have Backbone models and want Knockout's data binding without rewriting the model layer, since the wrapping is bidirectional and computed properties can be layered on top. Do not adopt it for a new project, because Backbone and Knockout are both legacy choices and the README itself frames the library around leveraging the work of both communities rather than replacing either.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 13 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The whole idea is one function call
The simple example is short enough to read in one breath. You create a Backbone model with some attributes, wrap it, and apply the bindings:
import Backbone from 'backbone';
import ko from 'knockout';
import kb from '@mcpeasy/knockback';
const model = new Backbone.Model({ first_name: 'Bob', last_name: 'Smith' });
ko.applyBindings(kb.viewModel(model));The HTML side is ordinary Knockout data-binding attributes on plain inputs. What the wrapper adds is that the values move in both directions between the input and the model, and to any other view model bound to the same model. The README's claim is that you get amazingly dynamic applications by applying a small number of simple principles, and leveraging the work from both the Backbone and Knockout communities, with full TypeScript support and type definitions included. There is no separate observable wiring step and no custom binding handler to write, which is what makes the integration cheap.
Computed properties go on top of the model
The advanced example shows what the wrapper enables beyond plain attribute binding. The view model is typed as an interface with observable fields and a computed property, and the `extend` option adds the computed:
const vm = kb.viewModel<PersonViewModel>(model, {
extend: (vm) => ({
full_name: ko.computed(() => `${vm.first_name()} ${vm.last_name()}`);
}),
});The extend option accepts either a function that receives the view model and returns the additions, or a plain object if you prefer, in which case the computed reads straight from the model with `model.get(...)`. The binding then uses the computed normally, and the greeting updates as you type. The API surface is a small set of core functions: `observable(model, key, options?)` for a single attribute, `viewModel(model, options?)` for all attributes, and `collectionObservable(collection, options?)` for an observable array over a Backbone collection. Named imports are supported as well as the default import, so you can pull in `viewModel` directly.
v2.0 renamed everything, and that is the migration story
The breaking change section is a pair of tables, and they are worth reading before you look at any code. v2.0 uses modern TypeScript conventions, and all option names and API properties changed from snake_case to camelCase. The core options are `static_defaults` to `staticDefaults`, `event_watcher` to `eventWatcher`, `view_model` to `viewModel`, `models_only` to `modelsOnly`, `auto_compact` to `autoCompact` and `sort_attribute` to `sortAttribute`. The plugin options went the same way, covering the locale manager, the event selector, and the validation options including `no_attach`, `$error_count` and `$active_error`. The migration is mechanical, since the names map one to one, and the repository has a MIGRATION.md file and a RELEASE_NOTES.md alongside the README for the details.
Disposal is automatic, and there is a statistics counter for leaks
The disposal story changed in 2.0 and it is the detail that shows the project has been used in real applications. The `dispose()` method is attached to view models and observables, and is called by `releaseOnNodeRemove` and by Knockout component disposal, so you no longer call a manual destroy. A separate helper `dispose(obj)` exists for plain objects that contain Knockback observables. Because automatic disposal removes the obvious leak, the library also ships a leak check facility: you create a `Statistics` instance, register it, take a snapshot before and after a route change, and diff the two to see what was still alive:
const stats = new kb.Statistics();
kb.setStatistics(stats);
const before = stats.snapshot();
// ... route change + dispose old page ...
const after = stats.snapshot();
const delta = stats.diff(before, after);
console.log(delta);That is a debugging tool rather than a feature, and it is the kind of tool a library only gets after people have hit the problem it solves.
Plugins for defaults, formatting, locale and validation
The plugin set covers the things you would otherwise write by hand. `defaultObservable<T>(observable, defaultValue)` supplies default values, `formattedObservable(format, ...args)` does two-way string formatting, `localizedObservable(value, options)` is locale aware, `triggeredObservable(emitter, event)` updates from an event, and `valueValidator(observable, validators)` validates values. The localization support is typed: you implement a `LocaleManager` interface with `get`, `getLocale` and `setLocale`, register it with `kb.setLocaleManager`, and retrieve it with `kb.getLocaleManager<MyLocaleManager>()`. The generics on the getter are the point, since they let the manager be typed as your own implementation rather than the interface, so the locale methods are strongly typed at the call site.
The package is now scoped and the test matrix is wide
The package manifest tells you how the project has been modernised. The published name is `@mcpeasy/knockback` at version 2.0.15, the project is an ES module with a CJS build and a UMD bundle, and the types are shipped alongside the CommonJS build. The build and test scripts run through `tsds`, and the test command chains three suites: type tests, browser tests and node tests. The browser suite runs a relationships matrix across test files, and the node suite is run under a specific engine version by an `nvu` command, with a separate engines script that tests across the supported Node versions. There is a packed-package test that installs the tarball and checks the types against the real published artifact, which is the kind of test that catches packaging mistakes that unit tests never see. The formatting is handled by biome.
Editorial conclusion
Adopt Knockback if you already have Backbone models and want Knockout's data binding without rewriting the model layer, since the wrapping is bidirectional and computed properties can be layered on top. Do not adopt it for a new project, because Backbone and Knockout are both legacy choices and the README itself frames the library around leveraging the work of both communities rather than replacing either. Verify first which version you take, because v2.0 renamed every option and API property from snake_case to camelCase, and check the package name, since the published package is @mcpeasy/knockback while the repository is kmalakoff/knockback.
Frequently asked questions
What does Knockback do?
It wraps Backbone models and collections in Knockout view models so that data-bound inputs transfer values bi-directionally to the model, and computed properties can be added on top. The core functions are `observable`, `viewModel` and `collectionObservable`.
What changed in Knockback 2.0?
All option names and API properties changed from snake_case to camelCase, so `static_defaults` became `staticDefaults`, `view_model` became `viewModel` and `sort_attribute` became `sortAttribute`, with the same change applied to the plugin options.
How do I install Knockback and its peers?
Install the package with `npm install knockback`, and the peer dependencies with `npm install backbone knockout underscore`. The published package name is `@mcpeasy/knockback`.
How do I dispose of a Knockback view model?
Disposal is attached automatically and is called by `releaseOnNodeRemove` and Knockout component disposal, so you no longer call destroy manually. A separate `dispose(obj)` helper disposes plain objects that contain Knockback observables.
Can Knockback help find memory leaks?
Yes. You create a `kb.Statistics` instance, register it with `kb.setStatistics`, take a `snapshot()` before and after a route change, and use `stats.diff(before, after)` to see what was still alive.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/kmalakoff-knockback)Was this useful?