October 5, 2026

GenUI: How AI Agents Build Interfaces (and How to Keep Them in Line)

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.

GenUI is a spectrum

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:

  • Static UI. Developers build fixed layouts ahead of time. The structure never changes. It is the least flexible option and the most predictable.
  • Declarative UI. The interface is described by a structured protocol, and it can change at runtime as long as it stays inside a defined schema.
  • Fully generated UI. The model creates the interface on demand, shaped entirely by context and user intent.

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.

What sits inside a GenUI agent

A GenUI agent is a stack of layers that compose into one working system:

  • The system prompt and skills define the agent's behavior, its intent, and the actions it can take.
  • The model does the reasoning, and MCP servers give it access to external data sources.
  • The widget catalog lists the components the agent may use, such as carousels, tabs, cards, lists, grids, text, images, and dividers.

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 JSON contract

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.

Why structured UI matters for production agents

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.

How the loop works

Every interaction makes a round trip to the agent and comes back as a rendered interface:

  1. The user interacts with a component, for example by tapping a button.
  2. The event updates a central, observable data model with the new state.
  3. That state is serialized and sent to the agent, which reasons over the new context and computes a response.
  4. The agent returns structured JSON, and the app renders the updated surface, ready for the next tap.

Under the hood: the Flutter GenUI SDK

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.
  • A Surface is a piece of UI the agent describes and the app renders.
  • Conversation manages events and orchestrates the whole generative UI process.

Connecting the agent

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.

Keeping GenUI fast

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.

Where this is useful

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.

Building GenUI with Celerik

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.

‍

Related articles

You may also like these

Subscribe to The Edge

Strategies on AI, data, and automation, plus success stories from leading businesses, straight to your inbox!

Let’s Create Your Own Success Story

Let’s discuss how we can deliver measurable impact for your business.
Book a call
© 2026 Celerik. All rights reserved.
SMS TermsPrivacy Policy