Why not a render layer
What a generative-UI protocol gives you, what it does not, and where the work actually is.
There are now open protocols for a model to describe an interface — Google's A2UI among them — and they are good. If you are choosing between one of those and this, the honest answer is that they solve a different part of the problem, and the part they solve is not the part that takes the time.
What a render layer gives you
A vocabulary for "draw a card with these fields", and a renderer for it. That is real work and it is worth not doing twice.
Rendel has one too. Twenty-one typed components, validated server-side against a schema before anything reaches a device, drawn natively in Flutter. If that were the whole product, a protocol you did not have to maintain would be the better choice and we would say so.
What is left over
Everything below is what a render layer does not have an opinion about, and every one of them is a decision someone has to make before a copilot can touch a real account.
Which of your functions may be called, and what it costs to call them. A typed manifest, a risk level per action, a confirmation card the server builds from your template and the parameters it validated — never from model output — and a record of every proposal and how it ended. A render layer will happily draw a button labelled "Refund £400"; what happens when it is pressed is the product.
Whether the tap was real. With server-mediated confirmation on, the tap is recorded here before your handler runs, and the client's word for it is ignored. That is a property of the wire and the database, not of a component.
What the model is allowed to see. Email addresses and phone numbers become typed placeholders before any model call; card-like numbers are dropped. The values are encrypted, stay in Frankfurt, and are substituted back only on the way to the device that owns the conversation, never into a URL field. A rendering protocol has no view on any of this, correctly — it is not its job.
The turn as a billable, resumable unit. One POST, one stream, one row of usage, an idempotency key that survives a retry, and a resume path for a stream that dies after the work was done. Most of the hard bugs in this system have lived here.
Everything after launch. Whose conversations these are, how long they are kept, who at the vendor has read one, what the copilot answered with nothing behind it, which action people keep declining.
The straight comparison
| A render protocol | Rendel | |
|---|---|---|
| Component vocabulary | Yes, and open | Yes, fixed at 21 and versioned |
| Bring your own components | The point of it | ComponentRegistry.register, but the catalog is the contract |
| Action execution | Out of scope | Typed, risk-levelled, confirmed |
| Confirmation you can trust | Out of scope | Server-built card, optionally server-recorded tap |
| Redaction before inference | Out of scope | Built in, not optional |
| Tenancy, quotas, billing | Out of scope | The reason there is a server |
| Transcripts, audit, analytics | Out of scope | The console |
| Vendor lock-in | None | Real. Read on. |
Where this leaves you
Use a render protocol if you have a backend team who want to own the action layer, the safety model and the operational surface, and you need a vocabulary rather than a service. That is a legitimate build, and it is most of a year.
Use this if what you want is the copilot working in your app next week, with the parts that are expensive to get wrong already decided.
The lock-in is real and worth naming. Your actions are declared in our SDK's shape, your conversations live in our database, and the catalog is ours. What reduces it: the manifest is MCP's tool shape verbatim, so your action definitions are portable; every component is documented field by field; and the export hands your whole app back as one file whenever you ask. We would rather say that plainly than have you find it in month four.
Are you going to support one?
Probably, as an input. The catalog is the contract for what a device can draw, and a protocol that describes a component we do not have is a component we cannot validate or render — so accepting arbitrary declarations would mean giving up the guarantee that a block reaching a device is one the device can draw. The likely shape is a mapping into the catalog where it fits, plus host-registered components for the rest, which already works today.
If this is blocking a decision for you, tell us what you need it for rather than which protocol you want. The answer is usually a component we should add.