Docs / Core / Rendering & VNodes
ven.js

Rendering & VNodes

Build a virtual DOM with venjs.<tag>() and mount it with venjs.render(). In a multi-page app each page file exports a component and the router renders the active one.

venjs.createElement(tag, props, ...children)

Creates a VNode. It is also exposed as window.v. Every venjs.div(), venjs.h1(), etc. is just a thin call to this.

venjs.createElement(tag, props, ...children) → VNode
// All equivalent
venjs.createElement("div", { class: "x" }, "hi");
venjs.div({ class: "x" }, "hi");
v("div", { class: "x" }, "hi");

Children

  • Children are flattened recursively (Infinity) and null/undefined entries are removed.
  • Primitive children (strings, numbers) become text VNodes automatically.
  • Nested arrays are supported: venjs.ul({}, [items.map(...), footer]).

VNode shape

{ tag, key, props, children, dom }
FieldMeaning
tagHTML tag name, or "TEXT_ELEMENT" for text nodes.
keyOptional stable key for list reconciliation.
propsAttributes, style, events, ref, onMount, onUnmount.
childrenArray of child VNodes (text nodes included).
domSet by the engine after the node is created in the real DOM.

venjs.render(container, componentFactory)

Mounts a component factory into a container element and keeps it patched. On the first run it clears the container and appends the DOM; on subsequent runs it diffs and patches.

const unmount = venjs.render(container, factory);
ArgumentDescription
containerA real DOM element (e.g. document.getElementById("app")).
componentFactoryA function returning a VNode, or a plain VNode. Pass a function so it re-evaluates on each effect run.

It returns an unmount function that stops the internal effect, calls onUnmount hooks, and clears the container.

const stop = venjs.render(app, MyComponent);
// later
stop(); // tear down
Note: venjs.render, venjs.mount, and venjs.ven are equivalent aliases. Prefer venjs.render.

Props & attributes

The engine classifies props into events, properties, and lifecycle hooks:

  • Events — any key starting with on (onclick, oninput, onmouseenter). Listeners are added/removed on change.
  • Properties — everything else that is not children, key, ref, onMount, onUnmount, or an event. These map to DOM properties or attributes.
  • LifecycleonMount, onUnmount.

Special prop handling

PropBehavior
class / classNameSets dom.className.
styleObject form is diffed against the previous style; removed keys are reset to "".
innerHTMLSet directly via dom.innerHTML.
value, checked, selected, disabledSet as DOM properties (so inputs stay controlled).
refA function called with the real DOM node: ref: (el) => ....
keyUsed for stable list reconciliation.
onMount / onUnmountLifecycle callbacks receiving (dom, vnode).
venjs.input({
  class: "field",
  style: { color: "teal", padding: "8px" },
  value: name.value,
  oninput: (e) => (name.value = e.target.value),
  ref: (el) => el.focus(),
  onMount: (el) => console.log("mounted", el),
  onUnmount: (el) => console.log("unmounted", el)
})

Reconciliation engine

patch(parent, oldVNode, newVNode, index) updates the real DOM minimally:

  • New node with no old → create and append, then fire onMount.
  • Old node removed → fire onUnmount and remove.
  • Different tag/key → replace and swap mount/unmount.
  • Text node → update nodeValue only when changed.
  • Same node → update props, then recurse into children. Excess old children are removed backwards to keep indices stable.