cDOM (Experimental)
A declarative, expression-based way to build reactive UIs.
Overview
The Computational DOM (cDOM) is a revolutionary way to describe user interfaces using reactive expressions that feel as natural as spreadsheet formulas. Instead of writing complex JavaScript logic to update your UI, you define the relationships between your data and your elements.
If you wish to explore the implementation further, you can visit the GitHub repository, browse the cdom directory, and check out the cdom unit tests.
Advantages
🛡️ Enhanced Security
cDOM strictly avoids eval() and direct HTML injection. By using a custom
high-performance parser and a registry of pre-defined helper functions, it provides a safe sandbox
for dynamic content, making it highly resistant to XSS attacks.
🤖 LLM Friendly
Large Language Models excel at generating structured data and formulaic expressions. cDOM's declarative nature and concise syntax (CDOMC) make it far easier for AI to generate correct, bug-free UI components compared to traditional JavaScript-heavy frameworks.
How It Works
cDOM uses a powerful path-based resolution system to link UI elements directly to reactive state.
cDOM paths can range from simple property access to complex, nested transformations. Consider this example:
currency(sum(map(filter($/orders, eq(_/status, 'paid')), _/total)...))
- $/orders: Access the global
orderscollection. - filter(..., eq(...)): Apply a filter to keep only "paid" orders using
_/status. - map(..., _/total): Transform each order into its
totalvalue using_/total. - ...: The Explosion Operator expands the array of totals into
individual arguments for the
sumfunction. - sum(...): Calculates the total of the exploded arguments.
- currency(...): Formats the result as a currency string.
Anatomy of a Path
A path in cDOM is a powerful string that describes how to navigate your reactive state and apply transformations.
Paths always start with a $.
- Root/Prefix:
$helperName(...args)or$helperName(...args): Call a registered helper function.$/<name>: Access the global named signal or state registry../: Access the local context (inherited from parent).../: Access the parent of the local context./: (Alias for global when not first item in path) Starts at the root of your state.
- Segments: Properties or indices (e.g.,
cart/items/0/priceorcart[0]/name). - Embedded Functions: Paths can contain function calls to transform data (e.g.,
$filter(/cart/items..., eq(_/status, 'paid'))). Functions are executed and their results can be passed to other helpers or used as element children. - Explosion Operator:
...expands an array into multiple arguments for a function, (e.g.,sort($/names...)) or extracts a property from every item in an array (e.g.,$/items...name). The extraction is effectively a shothand formap. - Placeholders:
_: Represents the current item during iteration (like inmap(_)).$event: Represents the event object in event handlers (e.g.,cdom-on:click="set(/current, $event.target.value)").
Examples
Representing a shopping cart total with reactive expressions:
// Example CDOM structure. Note: cDOM paths are not quoted, a special parser is used for files eding in .cdom
{
div: {
children: [
{ h2: "Shopping Cart" }, // Static header
{ ul: {
// Map over the items in the cart: name - $price
children: $map(/cart/items, li(_/name, ' - $', _/price))
}},
{ p: {
class: "total",
// Calculate the total by summing item prices
children: ["Total: $", $sum(/cart/items...price)]
}}
]
}
}
Expected HTML Output
<div>
<h2>Shopping Cart</h2>
<ul>
<li>Apple - $1.00</li>
<li>Orange - $2.00</li>
</ul>
<p class="total">Total: $3.00</p>
</div>
Actual Rendering
Shopping Cart
- Apple - $1.00
- Orange - $2.00
Total: $3.00
Comparison to Excel
Think of your UI as a spreadsheet. In Excel, if Cell C1 has the formula =A1+B1, C1 updates
automatically whenever A1 or B1 changes.
cDOM brings this exact paradigm to the web. Every attribute and text node can be a "cell" that computes its value based on other "cells" (reactive signals).
| Feature | Excel | cDOM |
|---|---|---|
| Reactive Unit | Cell | Signal / State Proxy |
| Formulas | =SUM(A1:A10) |
sum($/items...) |
| Path Resolution | Cell References (A1, $B$2) | JSON Paths (./name, $/global) |
| Recalculation | Automatic on change | Automatic on change |
Events and Interaction
cDOM is designed to handle user interactions gracefully, whether in a traditional standalone application or in a dynamic, LLM-driven environment.
Manual Implementation (Standalone Apps)
Developers can use the cdom-on: directive to bind expressions directly to DOM events. This
allows for clear, declarative event handling that updates local state or calls helper functions.
<button cdom-on:click="increment(/count)">Click Me</button>
LLM-Generated Interaction
Because cDOM represents the UI as data, an LLM can easily generate event handlers to register its interest in specific user actions. Instead of writing complex JavaScript observers, the LLM simply includes event handlers that notify the server.
// Example of an LLM 'registering' for a click event. Note the fetch url is quotes so it is not mistaken for a CDOM signal or state reference.
{
button: {
"cdom-on:click": "fetch('/api/notify', { method: 'POST', body: { $event } })",
children: ["Notify LLM"]
}
}
By generating these attributes on specific elements, the LLM effectively "subscribes" to the behavioral data
it needs to reason about the user's next steps. The $event placeholder ensures the LLM receives
the
full context of the interaction.
The Interaction Lifecycle
🤖 LLM-Driven Flow
- LLM generates the initial UI structure as JSON.
- HTTP Server receives the JSON and serves it to the Client.
- Client renders the cDOM and activates local reactivity.
- User Interacts (e.g., clicks a 'notify' button).
- Client sends the interaction data back to the HTTP Server.
- HTTP Server relays the event context to the LLM.
- LLM processes the event and sends a Patch/Update back to the HTTP Server.
- HTTP Server relays the update to the Client.
- Client merges the update, instantly refreshing the UI.
🏠 Standalone Flow
- Static cDOM is defined in the Client's source code or local files.
- Client activates the UI locally on page load.
- User Interacts (e.g., clicks a local toggle).
- Local State is updated immediately.
- UI Recalculates based on reactive expressions (0 latency).
- HTTP Server is only contacted for persistence or traditional API calls.
Helper Functions
cDOM comes with a rich set of built-in helpers that cover most common UI logic and data transformations. Developers can also register their own custom functions to extend cDOM's capabilities:
// Register a custom function
cDOM.register(f, name=f.name);
For security and deterministic behavior, cDOM operates in a sandbox. It cannot access
globalThis or arbitrary window properties. Only functions explicitly registered via
cDOM.register() are available for use within cDOM expressions.
Math
Basic arithmetic and mathematical operations.
+, add, -, sub, *, mul, /, div, round, ceil, floor, abs, mod, pow, sqrt
Stats
Aggregate results from arrays of numbers.
sum, avg, min, max, median, stdev, var
String
Text manipulation and formatting.
upper, lower, trim, capitalize, titleCase, contains, startsWith, endsWith, replace, split, len, join, default
Array
Collection processing and iteration.
count(len), map, filter, find, unique, sort, reverse, first, last, slice, flatten, join
Logic & Comparison
Boolean logic and value comparison.
if, and(&&), or(||), not(!), eq(==, ===), neq(!=), gt(>), lt(<), gte(>=), lte(<=), between, in
Conditional Aggregates
Statistical functions that take a filter predicate.
sumIf, countIf, avgIf
Formatting
Display helpers for various data types.
number, currency, percent, thousands
DateTime
Date creation, parsing, and arithmetic.
now, today, date, formatDate, year, month, day, weekday, addDays, dateDiff
Lookup
Data retrieval from indexed structures.
lookup, vlookup, index, match
State Mutation
Directly modify reactive state from event handlers.
set, increment(++), decrement(--), toggle(!!), push, pop, assign, clear
Network
Data fetching and network requests.
fetch(url, options?)
The fetch helper simplifies network requests by handling body serialization.
If the body is a non-null object, it is automatically stringified as JSON and the
Content-Type is set to application/json (if not already set).
Otherwise, the body is converted to a string and Content-Type is set to
text/plain.