Registration API (vanilla)
Register a custom collaborative element with one initializer object. Use register for one element and define for a reusable capability.
import { playhtml, html, svg, repeat, classMap, styleMap, nothing } from "playhtml";
playhtml.register(elementOrId, init)
Section titled “playhtml.register(elementOrId, init)”Binds an initializer to one element and returns a handle. Pass an element when you already have the DOM node. Pass its id when you need to register before the element exists. Both forms work before or after playhtml.init().
const counter = document.getElementById("my-counter");
const handle = playhtml.register(counter, {
defaultData: { count: 0 },
onClick: (_event, { setData }) => {
setData((data) => {
data.count += 1;
});
},
updateElement: ({ element, data }) => {
element.textContent = String(data.count);
},
});
- The element form requires an HTML element with a stable, non-empty
id. - The id form binds automatically when an element with that id appears.
- The
can-playattribute is optional.registersupplies the custom capability. - Re-registering the same id replaces its initializer.
playhtml.define(capabilityName, init)
Section titled “playhtml.define(capabilityName, init)”Registers a reusable capability under an attribute name. Matching elements already on the page bind immediately, as do matching descendants rendered by a view. For other elements added after initialization, call setupPlayElement(element). define is the imperative counterpart of init({ extraCapabilities }).
playhtml.define("can-note", init);
Defining a name that collides with a built-in capability throws. Each bound element still needs a unique id.
playhtml.getHandle(elementId, capability?)
Section titled “playhtml.getHandle(elementId, capability?)”Returns a handle for any bound element. Because data is keyed by capability and id, pass the capability name when one element carries more than one.
const handle = playhtml.getHandle("card-1", "can-move");
The init object
Section titled “The init object”The full annotated property list is on Element API. Both updateElement and view receive the callback context.
defaultData must be an object (or a function that returns one), not a bare value like 0 or "". Use { count: 0 }, not 0.
A valid initializer provides exactly one update path — view or updateElement.
The view context
Section titled “The view context”view receives ctx (Callback context). Drive ctx.setData from @click handlers, not during render.
onMount gets getters (getData(), getElement(), …) instead of live values. See Element API → onMount for the playhtml.ready pattern.
PlayElementHandle
Section titled “PlayElementHandle”Returned by register and getHandle. Reads and writes resolve the live handler lazily — a handle obtained before binding works once the element exists.
{
id,
getElement(), // null until bound
getData(), // undefined until bound
setData(next),
setLocalData(next),
setMyAwareness(next),
requestUpdate(), // no-op without a view
unregister(), // detach + run onMount cleanup; shared data is kept
}
A write through a handle whose element hasn’t bound yet is dropped (with a dev-mode warning); reads return undefined.
Re-exported lit-html helpers
Section titled “Re-exported lit-html helpers”playhtml re-exports the lit-html pieces a view needs. unsafeHTML is intentionally not exported, so interpolated values stay auto-escaped.
| Export | Use |
|---|---|
html | the tagged template for view output |
svg | SVG fragments (e.g. <path> inside <svg>) |
repeat(items, keyFn, template) | keyed lists — key by a stable unique id |
classMap(obj) | conditional classes |
styleMap(obj) | conditional inline styles (safer than a style string) |
nothing | render nothing (or just return null / undefined) |
See the lit-html templating guide for the full template syntax.