docs / Language
Reactivity
Tracked cells, the cell API, microtask-batched scheduling, effects, and the flushSync/tick escapes. No virtual DOM.
Vesk reactivity is built on tracked cells created with `track()`. Reading a cell inside a component body or `effect()` subscribes; writing a cell schedules an update. There is no virtual DOM: the compiler emits per-cell DOM update code.
Cell API
| Function | Purpose |
|---|---|
| track(initial) | Create a reactive cell |
| get(cell) | Read a value (subscribes inside effects) |
| set(cell, v) | Write a value; Object.is-guarded, schedules subscribers |
| increment(cell) / decrement(cell) | Post-increment/decrement a numeric cell; returns the original value |
| untrack(fn) | Run fn without subscribing |
| peek(cell) | Read a cell value without subscribing |
| derived(fn) | Computed cell that re-runs fn when its dependencies change |
| effect(fn) | Run fn now and on every dependency change |
| flushSync(fn) | Run fn with the scheduler in synchronous mode |
| tick() | Resolve after the next animation frame |
| on_destroy(fn) | Register teardown cleanup for the current block/component |
All of these are auto-imported from `@vesk/runtime` when used inside components — no import statement needed.
Scheduler semantics
- `set()` does not update the DOM synchronously — updates are microtask-batched, and multiple writes in one turn produce one flush.
- `effect()` runs immediately on creation, then on dependency change.
- `flushSync(fn)` flushes pending updates, runs fn with immediate DOM writes, and restores the async mode afterwards.
- `await tick()` resolves after the frame the flush has painted.
- The scheduler guards against effect loops — after 1001 flush rounds it throws: "Maximum update depth exceeded. This typically indicates that an effect reads and writes the same piece of state."
`batch` does not exist in the runtime. For synchronous multi-write flushes use `flushSync(fn)` — importing `batch` from `@vesk/runtime` does not resolve.
Example
component Counter {
let &[count] = track(0);
effect(() => {
console.log("count is", count);
});
<button onClick={() => count++}>
Count: {count}
</button>
}