ResonantJs Documentation
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.
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
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
});
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.
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:
| Method | Description |
|---|---|
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);
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.
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>
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>
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>
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
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;
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
| Action | Trigger |
|---|---|
added | Item pushed or inserted |
removed | Item deleted or spliced out |
modified | Item property changed |
updated | Entire array replaced via .update() |
filtered | Array filtered via .filterInPlace() |
JavaScript Methods
| Method | Description |
|---|---|
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
| Attribute | Description |
|---|---|
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
| Method | Description |
|---|---|
.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