Guides
Conventions for real apps: exhaustible UI states, safe JSON decode, imperative islands, capability-bound effects.
Do not scatter empty / loading / error as ad-hoc ifs. Fold into an ADT, then match once in the component.
type CatalogView =
| LoadingView
| ErrorView(string)
| EmptyCatalog
| CatalogRows(Product[])
function ProductPanel() {
return match catalogView() {
LoadingView => <div>Loading…</div>,
ErrorView(msg) => <div>{msg}</div>,
EmptyCatalog => <div>No products</div>,
CatalogRows(ps) => <ul>{ps.map(p => <li>{p.name}</li>)}</ul>,
}
}
parseJson → Result; Ok payload is unknown. Narrow field-by-field or whole-shape match. No as wash.
Editors / third-party widgets: bindImperative + cleanup on dispose. See living demos under src/language/desk/.
Do not call fetch / storage* / timers as free functions on the render path — put them in store methods/handlers (capability rules C12).
Authoritative: docs/19-app-scaffold.md · host table: HOST-HANDOFF.md.