The entire runtime weighs roughly 855 bytes minified and ships as a native ES module. It structures reactivity around two core functions: a read utility that connects CSS selectors to state properties, and a write utility that batches property mutations before applying changes to the document.
Features
- Reactive bindings between state and existing DOM selectors.
- Automatic dependency tracking at the property level.
- Batched property changes inside each
write()call. - Nested plain-object state tracking.
How to Use mador.js
Installation
Install the npm package:
npm install @marsbos/mador
Import it into an ES module:
import mador from "@marsbos/mador";
You can also directly import the module from a CDN:
import mador from "https://cdn.jsdelivr.net/npm/@marsbos/mador/dist/mador.js";
Basic Usage
Define an initial state object, bind it to matching DOM nodes, and trigger mutations on user interaction:
<button id="increment">Increment</button>
<p class="counter"></p>
<p class="triple-counter"></p>
<script type="module">
import mador from "https://cdn.jsdelivr.net/npm/@marsbos/mador/dist/mador.js";
const [read, write] = mador({
count: 1,
});
read(
".counter",
(element, count) => {
element.textContent = `Count: ${count}`;
},
(state) => state.count,
);
read(
".triple-counter",
(element, count) => {
element.textContent = `Triple: ${count * 3}`;
},
(state) => state.count,
);
document.querySelector("#increment").addEventListener("click", () => {
write((state) => {
state.count++;
});
});
</script>
Bind Multiple State Values
Return several values from the state reader when one DOM update depends on more than one property.
const [read, write] = mador({
name: "Ada",
count: 3,
});
read(
".summary",
(element, [name, count]) => {
element.textContent = `${name}: ${count}`;
},
(state) => [state.name, state.count],
);
Public API
Calling mador() returns the two public state functions:
const [read, write] = mador(initialState);
mador(initialState)creates the reactive state store and returns[read, write].read(selector, updateFn, valueFn)registers a DOM binding.valueFnreads the state dependencies, whileupdateFnreceives each matching element and the resulting value.write(updateFn)changes state through the reactive store. Property changes made during one call are processed together.
A read() binding runs once when registered. Later writes rerun it when changed state paths overlap the dependencies recorded by its valueFn.
A property can also receive a function when its next value depends on the previous value:
write((state) => {
state.count = (count) => count + 1;
});
Alternatives & Related Resources
- Minimal State Management for Vanilla JavaScript – Tiny Signals
- Zero-Dependency Reactive DOM from JSON Data – Sprout
FAQs
Q: Can one binding depend on several state properties?
A: Yes. Read each required property inside the third read() callback and return the values needed by the DOM update.
Q: Why does state.items.push() not trigger an update?
A: Arrays are not recursively proxied. Assign a replacement array through write() when array contents change.
Q: What happens after a bound DOM element is removed?
A: On a later relevant state update, a binding with no matching DOM elements is removed from the active runner list.
The post mador.js: Tiny Reactive DOM Runtime for Vanilla JS appeared first on CSS Script.
Discover more from RSS Feeds Cloud
Subscribe to get the latest posts sent to your email.
