Rendering and lifecycle

white-label-view

Render state through explicit browser or server lifecycles, with optional first-party JSX and no component-framework runtime.

Install and requirements

  • Node.js ^22.18.0 or >=24.11.0 for installation/development/server rendering
  • Browser DOM only for the default browser entrypoint
  • No DOM globals required by white-label-view/server
import View from 'white-label-view';
import View from 'white-label-view/server';
import {raw} from 'white-label-view/jsx-runtime';

Rendering contract

Browser View templates must resolve to exactly one DOM element, one trusted single-root HTML string, or White Label JSX that becomes one root element. Invalid or multi-root output throws TypeError without replacing the last successful root.

Server View accepts trusted HTML strings or White Label JSX and stores the most recently rendered HTML for toString(). It does not emulate DOM nodes, mounting, delegated events, focus, or animation frames.

const view = new View({
    parentElement: document.querySelector('main'),
    model,
    template: data => <section>{data.name}</section>
}).initialize();

Browser lifecycle

initialize() performs a synchronous render. A successful mount/replacement establishes model binding, calls addListeners(), then afterMount(). Before replacement or destruction, removeListeners() runs and owned children are cleaned up.

Equal attached output can preserve the existing root. Use update(element, data) for deliberate in-place changes; return false to fall back to normal rendering.

class MenuView extends View {
    addListeners() {
        this.delegated.on('click', 'a', this.handleClick);
        return this;
    }
}

Model binding and batching

A model is treated as observable only when it exposes on() and removeListener(). If it exposes get(), View passes get() to the template; otherwise it passes the model itself.

batchUpdates: true coalesces model-triggered browser renders into one requestAnimationFrame. Manual render() stays synchronous. Server rendering is always synchronous.

JSX runtime and trust

The optional automatic JSX runtime escapes child text and attribute values by default. It supports fragments, arrays, function components, boolean attributes, className, htmlFor, and style objects.

raw() is an explicit trust escape hatch. Pass only markup the application already trusts or has sanitized. Direct HTML-string templates are also caller-trusted input.

const template = () => <section>{raw('<strong>Trusted</strong>')}</section>;

Browser constructor settings

new View(settings?)

Create a browser View.

ParameterTypeDefaultDescription
parentElementElementParent that owns the rendered root.
elementElementExisting root to adopt.
modelobjectData source; observable when it exposes on/removeListener.
template(data) => string | Node | JSXMarkupRender function; browser output must resolve to one element.
update(element, data) => booleanOptional in-place update hook.
batchUpdatesbooleanfalseCoalesce model-driven renders into an animation frame.
Returns
A View instance.
Runtime
Browser

Browser View API

update(element, data)

In-place update hook used before template replacement.

Returns
boolean; true means handled, false uses normal template rendering.
Runtime
Browser

setModel(model?)

Move model binding and synchronously render current data.

Returns
The same View instance.
Runtime
Browser

initialize()

Perform the initial synchronous render and lifecycle setup.

Returns
The same View instance.
Runtime
Browser

requestRender()

Render now or queue one animation-frame render when batching is enabled.

Returns
The same View instance.
Runtime
Browser

addChild(child)

Give this View cleanup ownership of another View without automatically mounting it.

Returns
The parent View.
Throws / rejects
TypeError for ownership cycles or a child already owned elsewhere.
Runtime
Browser

releaseChild(child)

Relinquish cleanup ownership without destroying the child.

Returns
The parent View.
Runtime
Browser

destroy()

Release listeners, binding, queued work, owned children, and the DOM root.

Returns
The same View instance; it may be initialized again.
Runtime
Browser

initializeModelBinding()

Subscribe once to an observable model's change event.

Returns
undefined.
Runtime
Browser

destroyModelBinding()

Release the owned model subscription and cancel queued rendering.

Returns
undefined.
Runtime
Browser

addListeners()

Subclass hook called after root installation.

Returns
The same View instance by default.
Runtime
Browser

removeListeners()

Subclass hook called before root replacement or destruction.

Returns
The same View instance by default.
Runtime
Browser

afterMount()

Subclass hook called after insertion/adoption and listener setup.

Returns
The same View instance by default.
Runtime
Browser

delegate(scope?)

Create a caller-owned delegated-event registry scoped to an element or the current root.

Returns
DelegatedEvents registry.
Runtime
Browser

render()

Synchronously mount, preserve, update, or replace the root.

Returns
The same View instance.
Throws / rejects
TypeError when template output does not resolve to exactly one browser element.
Runtime
Browser

DelegatedEvents API

delegated.on(type, selector, callback, options?)

Register a native delegated event listener. once is consumed only by a matching event.

Returns
The same DelegatedEvents registry.
Runtime
Browser

delegated.off(type, selector?, callback?, options?)

Remove matching delegated registrations, optionally scoped by selector/callback/capture.

Returns
The same DelegatedEvents registry.
Runtime
Browser

delegated.clear()

Remove every native and abort listener owned by the registry.

Returns
The same DelegatedEvents registry.
Runtime
Browser

Server View API

setModel(model?)

Move model binding and synchronously render current data.

Returns
The same server View.
Runtime
Node.js / DOM-free

initialize()

Render current state and initialize the server lifecycle.

Returns
The same server View.
Runtime
Node.js / DOM-free

addChild(child)

Register child cleanup ownership.

Returns
The parent server View.
Throws / rejects
TypeError for ownership cycles or conflicting ownership.
Runtime
Node.js / DOM-free

releaseChild(child)

Release ownership without destroying the child.

Returns
The parent server View.
Runtime
Node.js / DOM-free

initializeModelBinding()

Subscribe to observable model changes.

Returns
The same server View.
Runtime
Node.js / DOM-free

destroyModelBinding()

Release the model subscription.

Returns
The same server View.
Runtime
Node.js / DOM-free

render()

Synchronously render the template into stored HTML.

Returns
The same server View.
Throws / rejects
TypeError for output that is neither a string nor White Label JSX markup.
Runtime
Node.js / DOM-free

toString()

Read the most recently rendered HTML.

Returns
HTML string, or an empty string before/after output is cleared.
Runtime
Node.js / DOM-free

destroy()

Destroy children, release subscriptions, and clear stored output.

Returns
The same server View.
Runtime
Node.js / DOM-free

JSX runtime API

raw(value)

Mark caller-owned markup as trusted so it is inserted without escaping.

Returns
RawMarkup.
Runtime
Browser + Node.js
  • Never pass untrusted user content directly.

isJSXMarkup(value)

Identify output created by the White Label JSX runtime.

Returns
Type-predicate boolean.
Runtime
Browser + Node.js

jsx(type, props)

Automatic JSX runtime entry point.

Returns
JSXMarkup.
Runtime
Browser + Node.js

jsxs(type, props)

Automatic JSX runtime alias used for multi-child output.

Returns
JSXMarkup.
Runtime
Browser + Node.js

jsxDEV(type, props)

Development JSX runtime alias.

Returns
JSXMarkup.
Runtime
Browser + Node.js

Fragment

Fragment token understood by the JSX runtime.

Returns
symbol.
Runtime
Browser + Node.js

TypeScript

View.Settings and View.Model expose the supported browser/server contracts.

Template data is unknown by design; application code should narrow domain data before accessing fields.

Security and trust boundaries

JSX escapes normal dynamic text/attributes; raw() and HTML-string templates are trusted caller input.

View does not provide a sanitizer. Sanitize untrusted HTML before crossing a trusted-markup boundary.

Design boundaries

View owns rendering mechanics and rendering lifecycle, not application state, routing, networking, CSS, sanitization policy, or application-wide events.

The browser entrypoint does not provide automatic hydration or claim to reconcile arbitrary server DOM with browser state.