How it compares

Four libraries that add behaviour to HTML you already have. Where they differ, and why.

Knockout, Alpine.js, petite-vue and domma-reactive all sit in the same place: bindings written into server-rendered HTML, no build step, no virtual DOM. The facts below come from each project's own documentation and the npm registry as of September 2026; there are no benchmarks here, because a benchmark of your page is the only one worth having.

At a glance

domma-reactive Knockout Alpine.js petite-vue
Latest on npm 1.2.0 (Sept 2026) 3.5.3 3.17.4 0.4.1 (Jan 2022)
Binding syntax data-bind-text="x.value" data-bind="text: x" x-text="x" v-text="x"
How expressions run parsed and walked - no eval new Function Function (standard build) new Function()
Strict CSP, no unsafe-eval yes, the only build via the separate Knockout Secure Binding provider via the separate CSP build, with restrictions not supported
Keyed lists key= required, rows keep their nodes foreach, by identity x-for with :key v-for
Reuse registerComponent, with slots ko.components, with $componentTemplateNodes Alpine.data() for shared data functions returning a scope, with $template

Content Security Policy

This is the difference that decides things for some teams. A policy of script-src 'self' without unsafe-eval blocks any library that turns strings into code.

  • Knockout: "Knockout's default binder uses new Function to parse bindings, ... which violates the CSP eval prohibition" - from the Knockout Secure Binding project, which exists to replace it.
  • Alpine.js: the standard build "uses Function declarations ... [which] still violate 'unsafe-eval'"; a CSP build avoids it but cannot run arrow functions, destructuring, template literals or the spread operator, and cannot reach globals such as Math or JSON.
  • petite-vue: "evaluates the expressions using new Function(), which may be prohibited in strict CSP settings" (README).
  • domma-reactive: has one build, and it has never used eval or Function - asserted in the test suite against the source and every bundle. What you give up is the same kind of thing the Alpine CSP build gives up: expressions are a deliberate subset, and anything bigger belongs in a computed. See expressions.

Coming from Knockout

domma-reactive covers what Knockout does - observables, computeds with read/write, extenders, $parent / $parents / $parentContext, components, slots and virtual <!-- ko -->-style comments - with different spellings on purpose.

Knockout domma-reactive
ko.observable(1), read o() observable(1), read o.value
text: name data-bind-text="name"
css: {on: isActive} data-bind-class="isActive && 'on'"
value: query data-model="query.value"
if: isOpen data-if="isOpen.value"
foreach: rows data-each="rows key=id"
<!-- ko if: x --> <!-- dm if: x.value -->
component: {name: 'x', params: {a: b}} data-component="'x'" data-param-a="b"
.extend({rateLimit: 300}) .extend({rateLimit: 300}) - the write is never delayed

The one habit to unlearn: Knockout unwraps if: show for you. Here data-if="show" names the observable and, since 1.2, warns and stays hidden. The full mapping is in the reference.

When to pick something else

  • You want a single <script> and the shortest possible markup, and your CSP allows unsafe-eval: Alpine is very good at that.
  • You want Vue's template syntax on a page, and will move to full Vue later: petite-vue's v- attributes carry over - though its README still describes it as "pretty new" and "use at your own risk", and npm's latest is from 2022.
  • You maintain a large Knockout application and nothing is broken: stay. The mapping above is there for the day you need a strict CSP.

Get it See the features