Most AI agents still answer in paragraphs. You ask for flight options and get a wall of text. You ask for a sales summary and get bullet points you have to copy into a spreadsheet yourself.
Generative UI, or GenUI, changes the output. The agent responds with an interface: a grid of cards, a form, a set of tabs with a button that does something. Your users click instead of read.
We recently ran an internal GenUI training for our engineering team, as part of our AI & Process Talks series. Here is what we walked them through, from the big picture down to the code.
When people hear "AI-generated interface," they often picture a model writing HTML from scratch in real time. That is one end of the range, and it is rarely where a production product should start.
UI generation sits on a spectrum of flexibility:
The further right you go, the more the interface can adapt and the less your team controls. Picking your spot on that line is the first architecture decision, and most of the others follow from it. For most business applications we'd start in the declarative middle: the agent decides what to show, and your team decides what it is allowed to show.
A GenUI agent is a stack of layers that compose into one working system:
If you are a business leader, the catalog is the part to care about, because it is the guardrail. The agent can arrange and fill the components in the catalog, and it cannot invent one your team has not approved.
The agent does not write code for the screen. It returns structured JSON that describes the UI it wants, and your app does the rendering.
In the example from our training, a user searches for travel destinations. The agent returns a JSON payload with a Grid component that holds several Card children, each with destination data such as a name, an image, and a label (Mendocino was one of them). The app parses that payload and maps it directly to real components. The user sees a travel results screen with images, labels, and a search field, and nobody on the team designed that layout for that query.
Because the agent sends a description and not code, three things get easier.
Security is the first. The agent can only send data mapped to pre-approved components. Nothing arbitrary gets executed, injected scripts have nowhere to go, and you can audit every element that was rendered. For companies in regulated industries, this tends to be the deciding factor.
The result also looks native. There are no iframes or embedded web views. The renderer uses your own component library, design system, and theming, so a generated screen looks like the rest of your product.
And it travels well. The UI description is platform agnostic, so a single JSON contract can target web, mobile, or desktop without rewriting the agent logic.
Every interaction makes a round trip to the agent and comes back as a rendered interface:
For the hands-on part of the training we worked with the GenUI SDK for Flutter, an alpha package from the Flutter team. It implements A2UI (Agent-to-User Interface), an open protocol Google introduced for agent-driven interfaces.
Six concepts do most of the work:
SurfaceController manages the lifecycle of UI components and processes the A2UI messages the agent sends.DataModel is the central observable store for all dynamic UI state on a surface.Catalog is the collection of CatalogItem entries that defines which widgets the AI is allowed to use.A2uiTransportAdapter parses raw text chunks from the LLM stream into structured generation events.Surface is a piece of UI the agent describes and the app renders.Conversation manages events and orchestrates the whole generative UI process.The minimal setup has four steps. You create a transport adapter to handle the raw LLM stream, implement the callback that forwards messages to your model, create a conversation that ties the controller and transport together, and then listen for surface events.
Here is a simplified sketch of that wiring, based on the training material:
// 1. Create the transport that parses raw LLM stream chunks
final transport = A2uiTransportAdapter(onSend: onSendToLLM);
// 2. Forward each ChatMessage to your LLM client and stream the response back
Future<void> onSendToLLM(ChatMessage message) async {
final stream = llmClient.generateContentStream(message);
await for (final chunk in stream) {
transport.addChunk(chunk); // parsed incrementally into GenerationEvents
}
}
// 3. Create the conversation that orchestrates everything
final conversation = Conversation(
controller: surfaceController,
transport: transport,
);
// 4. Listen for surfaces the agent adds or removes
conversation.events.listen((event) {
if (event is ConversationSurfaceAdded) onSurfaceAdded(event);
if (event is ConversationSurfaceRemoved) onSurfaceRemoved(event);
});The onSend callback is the bridge between the conversation and the model. Because every chunk goes to transport.addChunk as soon as it arrives, the UI starts rendering while the model is still generating. Users see components appear progressively and do not wait for the full response.
The event listener keeps the screen in sync with the agent. When the agent produces a new surface, ConversationSurfaceAdded renders it. When a surface is no longer needed, ConversationSurfaceRemoved cleans it up.
Generating an interface takes more model output than generating a sentence, so latency is the first thing users will notice. These are the strategies we gave the team.
Stream everything. Render components as they arrive instead of holding the screen until the whole payload is complete.
Cache what you can. Being able to generate a new UI every time does not mean you should. If the context has not really changed, reuse the surface you already have.
Narrow the scope. If the model has five ways to build the same screen, it will spend time choosing. Pick one preferred component for each job in your catalog. The model generates faster and more consistently, and your QA team gets output that is easier to test.
GenUI fits wherever users need to act on what an AI finds. Search and discovery flows, internal dashboards that change with each question, onboarding that adapts to the person filling it out, and operations tools where the next step depends on the last answer are all good candidates.
It is a poor fit for screens that should never change, like a checkout flow or a compliance form. Those belong at the static end of the spectrum, and most real products will have screens at both ends.
At Celerik we build AI agents and AI-powered products for companies that need them to work in production, inside their existing systems, with security and compliance designed in from the start. GenUI is one of the patterns our teams now use when an agent's output should be something people can click on.
If you are figuring out where generative UI fits in your product, or whether it fits at all, let's talk.