Structive: The Structure Path to Unified UI and State.

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 Works

Examples

Shopping Cart Component

Demonstrates looping with wildcards, derived state via getters, and direct state mutation for item deletion.

US State Population Aggregation

Shows aggregating and displaying hierarchical data (states & populations) using structure paths for reactive totals.

Radar Chart Example

Visualizes metrics in a radar chart with reactive updates driven by structure paths.

The Philosophy: One Source of Truth

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."

What is a 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.

Direct & Consistent Relationship

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.

Wildcard Abstraction

Use wildcards like "users.*.name" to abstractly denote elements within a list, allowing the same operation on all items within an array.

Key Features

Zero Boilerplate

Developers only need to declaratively describe how the UI should look in a given state and directly modify state as pure JavaScript objects.

Virtual DOM Free

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.

True Reactivity with Proxies

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 as Getters

Derived states (values computed from primary state) are defined simply as getters using structure paths, maintaining the simple relationship between UI and data.

Simple, Declarative Code

Based on a Shopping Cart Component Example.

HTML Template

<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>
                    

JavaScript Logic

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);
  }
}
                    

How It Works Under the Hood

1

Define State & UI

Create a JS class for your state and an HTML template with structure paths like {{ user.name }}.

2

Proxy Wraps State

The framework wraps your state object in a Proxy to detect any changes made to properties, even deeply nested ones.

3

Direct Updates

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.