ResonantJs Documentation

Getting Started

ResonantJs is a lightweight (~18 KB minified) reactive data-binding library for vanilla JavaScript. Bind plain JS variables to HTML elements using simple res attributes — the DOM updates automatically when values change. No build step, no virtual DOM, no framework required.

Installation

Install via npm or include directly from a CDN:

# npm
npm install resonantjs

# CDN (add to your HTML)
<script src="https://unpkg.com/resonantjs@latest/resonant.min.js"></script>

Quick Start

Create a Resonant instance, register a variable, and bind it to the DOM:

<span res="greeting"></span>

<script src="https://unpkg.com/resonantjs@latest/resonant.min.js"></script>
<script>
  const res = new Resonant();
  res.add('greeting', 'Hello, World!');

  // Update the variable — the DOM updates automatically
  greeting = 'Hello, ResonantJs!';
</script>

That's it. Any element with res="greeting" will reflect the current value of greeting in real time.

Browser Support

Chrome 60+, Firefox 55+, Safari 12+, Edge 79+, and modern mobile browsers.

Live Demo

These demos are running ResonantJs right here on the page. Open your browser console to inspect the reactive variables.

Counter

Variable count is reactive. Click the buttons or type count = 42 in your console.

Todo List

No tasks yet. Add one above!
task(s)
Scalar Binding

Bind primitive values (strings, numbers, booleans) to DOM elements. Assigning a new value updates every bound element instantly.

const res = new Resonant();
res.add('name', 'World');

// All <span res="name"> elements now show "World"
name = 'ResonantJs';
// They now show "ResonantJs"

Two-Way Input Binding

When res is applied to an <input>, <textarea>, or <select>, the binding is two-way. Typing into the input updates the variable, and changing the variable updates the input.

<input res="name" placeholder="Type your name" />
<p>Hello, <span res="name"></span>!</p>

Multiple Variables at Once

res.addAll({
  firstName: 'John',
  lastName: 'Doe',
  age: 30
});
Object Binding

Objects are wrapped in a deep proxy. Changing any property triggers a targeted DOM update for elements bound to that property via res-prop.

res.add('user', { name: 'Alice', email: '[email protected]' });

// Bind in HTML:
<div res="user">
  <span res-prop="name"></span>
  <span res-prop="email"></span>
</div>

// Update a single property — only that element re-renders:
user.name = 'Bob';

Nested objects are also deeply proxied, so user.address.city = 'NYC' works as expected.

Array Rendering

Arrays use template-based rendering. The first child element inside a res container acts as the template. ResonantJs clones it for each array item and uses selective re-rendering — modifying one item only updates that item's DOM subtree.

res.add('tasks', [
  { text: 'Learn ResonantJs', done: false },
  { text: 'Build something', done: false }
]);

// HTML template:
<div res="tasks">
  <div>
    <span res-prop="text"></span>
  </div>
</div>

Mutating Arrays

Standard array methods and ResonantJs-specific methods are all reactive:

MethodDescription
push(item)Add item to end
splice(i, n)Remove/insert items
.set(index, value)Replace item at index
.delete(index)Remove item at index
.update(newArray)Replace entire array contents
.filterInPlace(fn)Mutating filter
.forceUpdate()Force full re-render
tasks.push({ text: 'Ship it', done: false });
tasks.set(0, { text: 'Learn ResonantJs', done: true });
tasks.delete(1);
tasks.filterInPlace(t => !t.done);
Computed Properties

Derived values that automatically recalculate when their dependencies change.

res.add('price', 100);
res.add('tax', 8.5);
res.computed('total', () => price + (price * tax / 100));

// <span res="total"></span> shows 108.5
price = 200;
// total automatically recalculates to 217

Computed properties can depend on other computed properties, forming dependency chains that resolve in the correct order.

Conditional Display

Use res-display to show or hide elements based on a JavaScript expression. The expression is re-evaluated whenever any referenced reactive variable changes.

<div res-display="tasks.length === 0">
  No tasks yet. Add one below!
</div>

<div res-display="tasks.length > 0">
  You have <span res="taskCount"></span> tasks.
</div>
Dynamic Styles

Apply CSS classes dynamically with res-style. The expression should return a space-separated string of class names.

<div res="tasks">
  <div res-style="done ? 'completed' : ''">
    <span res-prop="text"></span>
  </div>
</div>

<style>
  .completed { text-decoration: line-through; opacity: 0.5; }
</style>
Event Handling

res-onclick

Attach click handlers. Inside an array template, the handler receives the current item as an argument.

// Define a global function
function toggleTask(item) {
  item.done = !item.done;
}

<div res="tasks">
  <div res-onclick="toggleTask">
    <span res-prop="text"></span>
  </div>
</div>

res-onclick-remove

Remove an array item by matching a property value on click:

<div res="tasks">
  <div>
    <span res-prop="text"></span>
    <button res-onclick-remove="text">Delete</button>
  </div>
</div>
Persistence

Pass true as the third argument to .add() to persist a variable to localStorage. The value is automatically saved on every change and restored on page load.

// Third argument enables localStorage persistence
res.add('theme', 'dark', true);

// Changes are saved automatically
theme = 'light'; // persisted to localStorage
CSS Selector Binding

Bind a reactive variable to elements matched by a CSS selector. Useful for third-party widgets or CMS markup where you can't add res attributes.

res.add('score', 0);
res.bindByCssSelector('score', '.score-display');

// All elements matching '.score-display' update when score changes
score = 42;
Callbacks

Listen for changes to a reactive variable with addCallback. The callback receives the current value, the affected item (for arrays), and the action type.

res.addCallback('tasks', function(currentValue, item, action) {
  console.log('Action:', action, 'Item:', item);
});

Action Types

ActionTrigger
addedItem pushed or inserted
removedItem deleted or spliced out
modifiedItem property changed
updatedEntire array replaced via .update()
filteredArray filtered via .filterInPlace()
API Reference

JavaScript Methods

MethodDescription
new Resonant()Create a new instance
.add(name, value?, persist?)Register a reactive variable. persist=true syncs to localStorage
.addAll({ name: value })Register multiple variables at once
.addCallback(name, fn)Listen for changes: fn(currentValue, item, action)
.computed(name, fn)Define a derived value that auto-updates
.bindByCssSelector(name, selector)Bind a variable to elements matched by CSS selector

HTML Attributes

AttributeDescription
res="varName"Bind element to a reactive variable
res-prop="key"Bind to a property of an object or array item
res-display="expr"Conditionally show/hide based on JS expression
res-style="expr"Apply dynamic CSS classes from JS expression
res-onclick="fnName"Click handler (receives array item if in list)
res-onclick-remove="prop"Remove array item by matching property on click

Reactive Array Methods

MethodDescription
.set(index, value)Update item at index
.delete(index)Remove item at index
.update(newArray)Replace entire array contents
.filterInPlace(fn)Mutating filter
.forceUpdate()Force full re-render of the list

Source code and issues: github.com/amurgola/ResonantJs