Modules describe their UI - Mutka draws it
An untrusted module runs in a Web Worker, so it can never hand a React component across the boundary. Instead it sends a serializable tree of nodes, and the host renders them with the same native Liquid Glass widgets the core uses. Modules ship logic, not markup.
You can't postMessage a component
A community module lives behind an isolation boundary: no DOM, no document, no way to mount a React tree into Mutka's window. That isolation is the whole point of the sandbox - but UI still has to come from somewhere. Everything crossing a worker boundary must survive the structured clone algorithm, and a component can't.
So a module never sends UI. It sends a description of UI: a small, serializable UINode tree. The host receives that JSON and renders it with its own trusted, on-brand components. The module decides what; Mutka decides how it looks - the same declarative idea behind SwiftUI, applied across a process boundary.
One tree, four surfaces
The same UINode vocabulary fills every place a module can show UI, so there's only one thing to learn. Declare a panel and fill it from setup with host.ui.render(id, node); open a modal with host.ui.modal(node); add a settings section the same way.
Status-bar items are dynamic - upsert one with host.statusbar.set(item) and a click can open a popover built from the very same node tree.
- Side panels - declarative panes docked left or right
- Modals & settings sections - same nodes, different surface
- Status-bar items with popovers - live, upsertable
How a click gets back to the module
Rendering is one half; interaction is the other.
- 01
Describe
The module builds a UINode tree - a button, a list, a form - and hands it to host.ui.render. Every interactive node carries an action id.
- 02
Render
The host draws the tree with native Liquid Glass widgets. The module never injects HTML, CSS or components - only JSON crosses the boundary.
- 03
Route back
When the user clicks or submits, the host routes the event by its action id back into the module's runtime, where the handler you registered with host.onUIEvent runs.
Forms are just a schema
A form is one form node carrying a FormSchema - a JSON-Schema Draft-7 subset, the standard wire format. The host renders the inputs, validates, and returns the collected values to your handler.
Authors can generate that schema from zod (z.toJSONSchema()); the host never imports zod, it just reads the schema. You describe the fields; Mutka builds the native form.
What modules render this way
Real dev-modules already use the declarative surface end to end.
Settings panes
The WebDAV module renders its whole connection form as a declarative settings section.
Inspector panels
Folder Inspector fills a side pane with a live, declarative summary of the current directory.
Status items
Dir Stats upserts a status-bar item whose popover is built from the same node tree.
Modals
Any module can pop a Liquid Glass modal with host.ui.modal(node) and close it with null.
Frequently asked
Why can't modules just use React?
Untrusted modules run in an isolated Web Worker with no DOM access - that isolation is what makes a denied permission physically unreachable. A React tree can't cross postMessage, so modules send a serializable UINode tree the host renders instead.
Can a module inject custom CSS or HTML?
No. Only JSON crosses the boundary. The host renders every node with its own trusted Liquid Glass components, so modules can't break the look or smuggle in markup.
How do forms work without a UI framework?
A form node carries a JSON-Schema (Draft-7 subset). The host renders the fields, validates input and returns the values. You can author the schema by hand or generate it from zod.
Sources & further reading
The standards and prior art behind UI-as-data.
- 1Structured clone algorithmMDN Web Docs
The rule that defines what can cross a worker boundary, and why a UINode tree can but a React component can't.
- 2JSON Schema Draft-07JSON Schema
The standard Mutka's FormSchema is a subset of: forms are described in an open wire format, not a proprietary one.
- 3SwiftUIApple Developer
Apple's declarative-UI framework, the design lineage of describing interfaces as data.
- 4
