Skip to content
This repository


Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Tiny reactive template engine

Fetching latest commit…


Cannot retrieve the latest commit at this time

Octocat-spinner-32 examples
Octocat-spinner-32 lib
Octocat-spinner-32 test
Octocat-spinner-32 .gitignore
Octocat-spinner-32 Makefile
Octocat-spinner-32 component.json
Octocat-spinner-32 package.json
Octocat-spinner-32 reactive.js


Reactive template engine for robust real-time rendering of model data changes.


With component:

$ component install component/reactive

With the stand-alone browser build:

<script src="reactive.js"></script>


reactive(element, object, [view])

Bind object to the given element with optional view object. When a view object is present it will be checked first for overrides, which otherwise delegate to the model object.

For example if you have the following HTML:

<h1 data-text="name"></h1>

And pass the following object as the second argument:

  name: 'Tobi'

The output will become:


However if you wish to manipulate the output or provided computed properties thae view object may be passed. For example an object of:

  first_name: "Tobi",
  last_name: "Ferret"

And a view of:

function UserView(user) {
  this.user = user;
} = function(){
  return this.user.first_name + ' ' + this.user.last_name;

Would produce:

<h1>Tobi Ferret</h1>

Typically a view object wraps a model to provide additional functionality, this may look something like the following:

function UserView(user) {
  this.user = user;
  this.el = reactive(tmpl, user, this);
} = function(){ ... }

Often a higher-level API is built on top of this pattern to keep things DRY but this is left to your application / other libraries.



Subscriptions allow reactive to know when an object's data has changed updating the DOM appropriately without re-rendering a static template. This means if you make manual DOM adjustments, append canvases etc they will remain intact.

By default reactive subscribes using .on("change <name>", callback) however it's easy to define your own subscription methods:

reactive.subscribe(function(obj, prop, fn){
  obj.bind(prop, fn);

reactive.unsubscribe(function(obj, prop, fn){
  obj.unbind(prop, fn);

Getting and Setting

You can make reactive compatible with your favorite framework by defining how reactive gets and sets the model.

By default reactive supports obj[prop] = val and obj[prop](val), but these can be changed with reactive.get(fn) and reactive.set(fn). Here's how to make reactive compatible with backbone:

reactive.get(function(obj, prop) {
  return obj.get(prop);

reactive.set(function(obj, prop, val) {
  obj.set(prop, val);


Bindings may be applied via interoplation on attributes or text. For example here is a simple use of this feature to react to changes of an article's .name property:


Text interpolation may appear anywhere within the copy, and may contain complex JavaScript expressions for defaulting values or other operations.

  <h2>{ name || 'Untitled' }</h2>
  <p>Summary: { body.slice(0, 10) }</p>

Reactive is smart enough to pick out multiple properties that may be used, and react to any of their changes:

<p>Welcome { first + ' ' + last }.</p>

Interpolation works for attributes as well, reacting to changes as you'd expect:

<li class="file-{id}">
  <p><a href="/files/{id}/download">Download {filename}</a></p>

Declarative Bindings

By default reactive supplies bindings for setting properties, listening to events, toggling visibility, appending and replacing elements. Most of these start with "data-*" however this is not required.


The data-text binding sets the text content of an element.


The data-html binding sets the inner html of an element.


The data-<attr> bindings allows you to set an attribute:

<a data-href="download_url">Download</a>


The on-<event> bindings allow you to listen on an event:

<li data-text="title"><a on-click="remove">x</a></li>


The data-append binding allows you to append an existing element:

<div class="photo" data-append="histogram">



The data-replace binding allows you to replace an existing element:

<div class="photo" data-replace="histogram">



The data-show and data-hide bindings conditionally add "show" or "hide" classnames so that you may style an element as hidden or visible.

<p data-show="hasDescription" data-text="truncatedDescription"></p>


Toggles checkbox state:

<input type="checkbox" data-checked="agreed_to_terms">

Writing bindings

To author bindings simply call the reactive.bind(name, fn) method, passing the binding name and a callback which is invoked with the element itself and the value. For example here is a binding which removes an element when truthy:

reactive.bind('remove-if', function(el, name){
  el = $(el);
  var parent = el.parent();
    if (this.value(name)) {

Computed properties

Reactive supports computed properties denoted with the < character. Here the fullname property does not exist on the model, it is a combination of both .first and .last, however you must tell Reactive about the real properties in order for it to react appropriately:

<h1 data-text="fullname < first last"></h1>

NOTE: in the future Reactive may support hinting of computed properties from outside Reactive itself, as your ORM-ish library may already have this information.


Some bindings such as data-text and data-<attr> support interpolation. These properties are automatically added to the subscription, and react to changes:

 <a data-href="/download/{id}" data-text="Download {filename}"></a>


Get creative! There's a lot of application-specific logic that can be converted to declarative Reactive bindings. For example here's a naive "auto-submit" form binding:

<div class="login">
  <form action="/user" method="post" autosubmit>
    <input type="text" name="name" placeholder="Username" />
    <input type="password" name="pass" placeholder="Password" />
    <input type="submit" value="Login" />
var reactive = require('reactive');

// bind

var view = reactive(document.querySelector('.login'));

// custom binding available to this view only

view.bind('autosubmit', function(el){
  el.onsubmit = function(e){
    var path = el.getAttribute('action');
    var method = el.getAttribute('method').toUpperCase();
    console.log('submit to %s %s', method, path);

For more examples view the ./examples directory.



Something went wrong with that request. Please try again.