Skip to content

Rendering and publication ​

Views return compiled HTML/ECR templates. Crystal retains state, readers, callbacks and region owners. The browser parses HTML and retains managed targets. Native documents own authenticated mounts and ordered mutation batches.

Compilation and identities ​

The template compiler runs at Crystal compile time and preserves ordinary static HTML. It lowers expressions into typed readers, callbacks and region factories. Exact byte offsets identify private element, text and region markers, so namespacing never rewrites marker-like application literals.

Each mounted template instance receives fresh monotonic numeric identities. Static composition and repeated rows have independent instances, even with equal local marker names. An outer paired region identifies each instance's output. Initial serialization escapes scalar values, hydrates string controls, and emits an expected-target manifest. The browser checks target kinds, uniqueness, parsed ownership and ordered sibling boundaries before registration is published.

Reactive publication ​

Each scalar binding and region selector owns a stable reactive consumer. Reactive getters record dependencies; setters invalidate only their subscribers. Helper methods participate in the enclosing reader. Events, lifecycle hooks and queued notifications run untracked.

Dirty scalar readers emit changed text, attribute or property values directly. Input acknowledgements also confirm the canonical server value. Unrelated readers do not run. Scalar updates do not construct an intermediate DOM tree or scan lifecycle membership.

Invalidation queues each retained consumer once. A flush orders the queued work by owner depth so a structural parent can retire descendants before they run. Rejected transactions return their live dirty work to the queue; writes after a reader's staged read remain pending after publication. Callback and input-value lookup use a sparse target index rather than traversing the retained graph.

Evaluation stages normalized values, dependency versions and structural candidates. All candidates must succeed before publication. A failed reader or duplicate key leaves committed output, callbacks and subscription edges intact. Application mutations made by a callback are not rolled back. A write after a rendering read remains pending for another dispatcher update.

Regions and lifetimes ​

Conditionals retain their active branch while selection is unchanged. Keyed lists retain rows by sibling key, move their ranges on reorder, and release removed rows. New objects at an existing key refresh lexical captures. Compatible shapes preserve DOM targets; changed shapes replace the item. Arbitrary struct equality cannot conceal changed captures.

Dynamic mount regions select a retained view object or nil. Selecting the same object preserves state, template and update scope. Selecting another object ends the previous lifetime. View ownership is independent of DOM nesting.

A structural transaction presents candidate membership to the native mount before publication. New views receive scopes and run hooks before commit. Failing hooks release started resources and discard the candidate. Removed scopes deactivate together before cleanup, preventing old jobs or subscriptions from delivering into a replacement mounting. Initial hooks may change selection; mounting checks membership after each hook and acknowledges only already-rendered invalidations.

Browser operations ​

InsertRange parses trusted HTML at the actual insertion parent. RemoveRange retires an owned sibling range. MoveRange moves retained nodes, preserving identities, focus and applicable selection. Static SVG and custom elements remain native browser markup. Parsed target manifests catch repairs that violate reactive ownership. Raw text, inert templates and direct table text have explicit binding restrictions.

The native bridge serializes events, update requests and acknowledgements. Newer queued input edits survive older replies. Mutations precede DOM effects and each batch is acknowledged before another revision proceeds. Document replacement invalidates previous mounts, callbacks and queued work.

Dev hotpatch ​

Development builds instrument eligible static text and presentation attributes as literal readers. Source signatures retain shape, dynamic expressions, callbacks, bindings and selectors. Compatible edits publish immutable data; each session synchronizes its own literal consumers on its normal UI dispatcher. The filesystem worker never invalidates view consumers.

The watcher uses Compiler.analyze_literals to obtain the compatibility signature and literal values without emitting executable Crystal source. Analysis and compilation share the same canonical signature projection.

A settled save is classified as a whole. Crystal edits, changed actions, structural edits and unpatchable sources request rebuilding. Response-only layouts rebuild because they have no retained session. Compatible literals in inactive branches are kept for their next mount. Production static HTML remains literal.

Implementation ​

Built with Crystal. Documented with VitePress.