inmake DOCS
Docs / Server / Element rendering

Element rendering

How element placements render inside pages.

Pages can place reusable library elements (store/{sid}/elements/{id}: fields + a Handlebars html fragment + css/js + sample) through kind:'element' schema fields. At render time each placement resolves via the reference resolver's renderElement callback:

  • Live placements ({type:'element', element, data}) read the element doc through the meta cache (setupTtlMs + hook-based eviction on element edits) and render the fragment with the placement's own data — an element edit propagates to every page placing it. The page binds the result as {{{ field.html }}}; the element's css/js are hoisted into the page shell (content-deduped) and, in non-live mode, become the /store/{sid}/css|js/element/{id}.* links above.
  • Embedded placements (embedded: true — the page template carries a frozen copy of the element's code) resolve to data only; no doc read.

Every element read uses one mask, core-lib's ELEMENT_RENDER_MASK (html, css, js, module, disabled) — renderElement, serveCode and the elements hook eviction share one meta-cache entry per element.

Module elements — the runtime layer and the render gate. An element doc with module: '{id}' needs that store module's backend (/m/{id}/**) and its shared code. The module's runtime doc store/{sid}/elements/{id}_runtime (runtimeElementId; {runtime: true, js, css?, disabled?}, written by the platform on install — docs/modules.md §8.2a) serves both:

  • Gate — renderElement reads the runtime doc through the meta cache and asks moduleGateOpen(runtime): disabled: true (the module is switched off) or no doc at all (not installed) → the placement renders nothing, like a deleted element (the rollout's moduleGate: 'open' option was removed on 2026-10-27). The fragment carries module (RenderedFragment.module, recorded even for an element with no css/js of its own).
  • Runtime layer — the page pass collects modulesOf(fragments) and prepends moduleRuntimeLayer(fragments, runtimes): one fragment per module whose runtime doc has a non-empty js and/or css (the module's global sheet; an empty key is omitted), with the runtime doc's own element identity — so linked mode serves them at /store/{sid}/{js|css}/element/{id}_runtime.{js|css}?v=… through serveCode (inline mode inlines them). Order, for scripts and stylesheets alike: the shared element-library layer, then the module runtimes, then the elements — a module's elements override its global css.
  • Freshness — enable / disable is a write to the runtime doc, i.e. an ordinary elements hook: its meta entry is evicted and render+deps are cleared, so the gate bites within seconds rather than a TTL.

Embedded placements see neither (which is why the console never embeds a module element). A page with no module element makes no extra read and is byte-identical.

Shared element-kit code joins only element-bearing pages. When at least one fragment of the page is an element, the renderer reads setup/elements (meta cache) and adds, before any per-element CSS (so elements can override) and before the element scripts that consume them:

  • the kit's Storage asset files (setup/elements.assets — shared library files left by the retired platform-kit import), linked as /assets/{storage_path}?v={version} in every mode (they ride the same-origin asset pipeline; in live mode serveAsset's 302 passthrough answers them), and
  • the inline shared css/js fields, emitted as an elements fragment — linked as /store/{sid}/css/elements.css / …/js/elements.js in cached mode, inline in live mode.

Element-free pages skip the setup/elements read and the extra requests entirely.