A simple, scalable framework that eliminates the virtual DOM, boilerplate, and complex middle layers. Just define the data path, and your UI is fully reactive.
See How It WorksDemonstrates looping with wildcards, derived state via getters, and direct state mutation for item deletion.
Shows aggregating and displaying hierarchical data (states & populations) using structure paths for reactive totals.
Visualizes metrics in a radar chart with reactive updates driven by structure paths.
We perceive "UI and State as different expressions of the same data." Therefore, they share the same data structure and are accessed via the same "Structure Path."
It's like an "address" used to uniquely reference hierarchical data like JSON objects using strings. A path like "product.name" points directly to the property inside the object.
The application's state is centralized. The UI's role is simply to draw this state. This prevents inconsistencies caused by managing data in multiple places.
Use wildcards like "users.*.name" to abstractly denote elements within a list, allowing the same operation on all items within an array.
Developers only need to declaratively describe how the UI should look in a given state and directly modify state as pure JavaScript objects.
By establishing a 1-to-1 relationship via Structure Paths, UI changes are pinpointed directly to the state, and state changes are pinpointed directly back to the UI.
State is wrapped in a Proxy. Using structure paths in set traps allows deep nesting changes to be detected easily, updating only the corresponding UI for high performance.
Derived states (values computed from primary state) are defined simply as getters using structure paths, maintaining the simple relationship between UI and data.
Based on a Shopping Cart Component Example.
<template>
<!-- Loop through items using a structure path -->
{{ for:cart.items }}
<tr>
<!-- Use wildcard (*) to reference current item -->
<td>{{ cart.items.*.product.name }}</td>
<!-- Two-way binding works just by defining the path -->
<td>
<input type="number"
data-bind="valueAsNumber: cart.items.*.quantity">
</td>
<!-- Display derived state (calculated in JS) -->
<td class="right">
{{ cart.items.*.price|fix,2 }}
</td>
<!-- Bind event handlers with paths -->
<td>
<button type="button"
data-bind="onclick: onDeleteItemFromCart">
Delete
</button>
</td>
</tr>
{{ endfor: }}
</template>
const products = [
{ id: 1, name: "Smart Phone", price: 100 },
{ id: 2, name: "Tablet", price: 200 },
{ id: 3, name: "Laptop", price: 300 },
{ id: 4, name: "Desktop", price: 400 },
{ id: 5, name: "Smart Watch", price: 50 }
];
export default class {
// Primary State: Pure JS Objects
cart = {
items:[
{ product: products[0], quantity: 1 },
{ product: products[1], quantity: 2 }
]
}
// Derived State: Defined as a getter using the path
get "cart.items.*.price"() {
// Loop context is handled automatically
return this["cart.items.*.product.price"] * this["cart.items.*.quantity"];
}
// Methods act as event handlers or logic
onDeleteItemFromCart(event, index) {
// Directly manipulate state array
this["cart.items"] =
this["cart.items"].toSpliced(index, 1);
}
}
Create a JS class for your state and an HTML template with structure paths like {{ user.name }}.
The framework wraps your state object in a Proxy to detect any changes made to properties, even deeply nested ones.
When a path changes (e.g., product.price = 200), the framework identifies the exact UI element bound to that path and updates it directly. No Virtual DOM diffing needed.