The problem
Build-timeif and for directives render once at compile time. Showing different branches or lists that change after user interaction needs either client-side templating or manual DOM create/destroy.
How Aero helps
With<script is:state>, structural directives reconcile against live state. The compiler still lowers templates to HTML, but the reactivity mount patches branches and list rows when bindings change.
Build-time and runtime structural directives use the same syntax. The difference is the presence of a state script and reactivity: true.
Reactive conditionals
if, else-if, and else choose one branch based on a state expression:
Reactive switch
Match a state value against case branches, with default as fallback:
Keyed for loops
Reactive lists need a stable key so Aero can move or reuse DOM nodes instead of re-rendering the whole list:
In-place list mutation
Keyedfor also reconciles when you mutate the same array, Set, or Map in place:
items = [...items, next]) and in-place mutation (items.push(next)) both notify the binding. Prefer immutable updates when you use view transitions or need a new reference for derived state.
Destructuring in for
The loop binding supports destructuring patterns:
Requirements
keymust evaluate to a string or number per row.- Duplicate keys in one reconciliation pass throw at runtime.
- The iterable may be an array,
Set,Map, any object with[Symbol.iterator], ornull/undefined(treated as empty). Maprows iterate as[key, value]entry pairs — use destructuring in the loop binding.
Keyed reconciliation preserves focus and element state inside rows when items reorder. Pair with view transitions in client scripts for animated list updates.
Output fidelity
Reactive bindings anchor to authored markup instead of injecting wrapper elements:- Switch —
data-aero-switchon the element withswitch="{ … }". - Keyed
for—data-aero-foron the parent container when the loop row is the only element child (e.g.<ul data-aero-for>with<li for>rows). ifchains —data-aero-ifon a parent wrapper when the chain is its only content; otherwise HTML comment boundaries (<!-- aero:if:N -->…<!-- /aero:if:N -->).- Text interpolation —
data-aero-texton an element whose sole child is reactive text; mixed content uses comment boundaries. Prefer the explicittext="{ expr }"attribute when you need a marker on a specific element.
ul > li and :nth-child predictable in the live DOM.