Sprachmodelle antworten nicht mehr nur mit Text, sondern mit ganzen UI-Strukturen.
Sobald ein Sprachmodell auf eine Benutzeranfrage nicht nur einen Fließtext liefern, sondern auch eine Liste von Flügen anzeigen, eine Eingabemaske einblenden oder ein Diagramm rendern soll, wird die Integration zwischen Agent und Frontend schnell unübersichtlich. Mit A2UI hat Google einen Standard vorgelegt, der es einem Sprachmodell erlaubt, statt reinem Text auch Komponenten und deren Daten zurückzuliefern – das Frontend übernimmt dann die Darstellung.
Dieser erste Teil der dreiteiligen Serie führt in das A2UI-Protokoll ein und zeigt anhand einer Angular-Demo, wie der mitgelieferte Renderer die vom Modell beschriebenen UI-Strukturen darstellt.
📂 Source Code (siehe Branch a2ui-dynamic)
Was ist A2UI?
A2UI steht für Agent-to-UI und ist ein von Google vorgestellter Standard, der die Lücke zwischen einem Agent und einer dynamisch generierten Oberfläche schließt. Im Kern beschreibt A2UI ein Vokabular vordefinierter Komponenten samt deren Eigenschaften sowie ein Nachrichtenformat, mit dem der Agent dem Client mitteilt, welche Komponenten anzuzeigen sind und mit welchen Daten sie zu befüllen sind.
Statt nur Text zurückzugeben, wählt das Sprachmodell aus einem Katalog vordefinierter Komponenten und liefert die zugehörigen Daten gleich mit. Der Client muss diese Komponenten nur noch rendern – und bestimmt dabei die konkrete Darstellung selbst. Der Agent legt somit das „was" fest, der Client entscheidet über das „wie". So wirken die dynamisch erzeugten UI-Fragmente nicht wie Fremdkörper, sondern fügen sich in das bestehende Design der Anwendung ein.
Im Gegensatz zu Ansätzen wie MCP Apps, bei denen der Agent vollständige Komponenten ausliefert, verhindert die strikte Trennung zwischen Struktur und Darstellung bei A2UI auch, dass der Client zur Laufzeit fremden Code laden muss.
Als dieser Artikel verfasst wurde, lagen zwei Versionen von A2UI vor: die stabile Version 0.8 und die künftige Version 0.9 als Draft. In diesem Artikel gehe ich bereits auf Letztere ein.
A2UI in Aktion: Beispiele für dynamisch generierte UIs
Eine dynamische A2UI-Antwort sehen wir am Beispiel einer Demo-Anwendung, in der ein dynamisches Dashboard anhand einer textuellen Beschreibung aufgebaut wird:

Die Anforderungen für das Dashboard werden in einem Textfeld hinterlegt:

Ein weiteres Beispiel ist ein Chatverlauf, indem sich das LLM entscheidet, über eine dynamische Frage weitere Informationen einzuholen:

Welche Komponenten enthält der A2UI Basic Catalog?
Die zentrale Idee von A2UI ist ein sogenannter Komponenten-Katalog. Ein solcher Katalog beschreibt eine Menge von Komponenten, die sowohl der Agent als auch der Client kennen. Der von A2UI definierte Basic Catalog umfasst eine ganze Reihe gängiger UI-Bausteine, die sich in folgende Gruppen unterteilen lassen. Die folgende Übersicht ist der Spezifikation entnommen:
| Kategorie | Komponente | Beispiel |
|---|---|---|
| Display | Text | Zeigt Text an. Unterstützt einfaches Markdown. |
| Image | Zeigt ein Bild von einer URL an. | |
| Icon | Zeigt ein vom System bereitgestelltes Icon aus einer vordefinierten Liste an. | |
| Video | Zeigt ein Video von einer URL an. | |
| AudioPlayer | Ein Player für Audio-Inhalte von einer URL. | |
| Layout | Row | Ein horizontaler Layout-Container. |
| Column | Ein vertikaler Layout-Container. | |
| List | Eine scrollbare Liste von Komponenten. | |
| Container | Card | Ein Container im Karten-Stil. |
| Tabs | Eine Reihe von Tabs, jeweils mit Titel und untergeordneter Komponente. | |
| Divider | Eine horizontale oder vertikale Trennlinie. | |
| Modal | Ein Dialog, der über dem Hauptinhalt erscheint und durch einen Button im Hauptinhalt ausgelöst wird. | |
| Input | Button | Ein anklickbarer Button, der eine Aktion auslöst. Unterstützt die Varianten „primary" und „borderless". |
| CheckBox | Eine Checkbox mit Label und booleschem Wert. | |
| TextField | Ein Feld für Texteingaben durch den Benutzer. | |
| DateTimeInput | Eine Eingabe für Datum und/oder Uhrzeit. | |
| ChoicePicker | Eine Komponente zur Auswahl einer oder mehrerer Optionen. | |
| Slider | Ein Schieberegler zur Auswahl eines numerischen Wertes innerhalb eines Bereichs. |
Welche Funktionen bietet A2UI?
Neben den eigentlichen Komponenten bietet A2UI auch Funktionen, die das LLM in den Komponentendefinitionen referenzieren kann. Auf diese Weise lassen sich Werte, die zur Laufzeit ermittelt oder aufbereitet werden müssen – beispielsweise formatierte Zahlen und Datumsangaben – deklarativ beschreiben, ohne dass das Sprachmodell die konkrete Logik selbst ausführen muss. Der Basic Catalog bringt eine Reihe häufig benötigter Funktionen wie formatNumber oder formatDate von Haus aus mit. Auch die folgende Übersicht stammt aus der Spezifikation:
| Kategorie | Funktion | Beschreibung |
|---|---|---|
| Validierung | required | Prüft, dass der Wert nicht null, undefined oder leer ist. |
| regex | Prüft, dass der Wert einem regulären Ausdruck entspricht. | |
| length | Prüft Einschränkungen für die Stringlänge. | |
| numeric | Prüft Einschränkungen für numerische Bereiche. | |
| Prüft, dass der Wert eine gültige E-Mail-Adresse ist. | ||
| Formatierung | formatString | Führt String-Interpolation von Datenmodellwerten und registrierten Funktionen durch. |
| formatNumber | Formatiert eine Zahl mit Tausendertrennung und Nachkommastellen. | |
| formatCurrency | Formatiert eine Zahl als Währungszeichenkette. | |
| formatDate | Formatiert ein Datum/eine Uhrzeit anhand eines Musters. | |
| Verknüpfung | and | Logische UND-Verknüpfung einer Liste boolescher Werte. |
| or | Logische ODER-Verknüpfung einer Liste boolescher Werte. | |
| not | Logische NICHT-Verknüpfung eines booleschen Wertes. | |
| Sonstige | openUrl | Öffnet eine URL in einem Browser. |
| pluralize | Wählt eine lokalisierte Zeichenkette basierend auf einer numerischen Anzahl aus. |
Wie funktionieren A2UI-Nachrichten und der Renderer?
Der Client stellt die Komponenten über einen Renderer zur Anzeige. Der Renderer ist die Instanz, die A2UI-Nachrichten entgegennimmt und die zugehörigen UI-Elemente erzeugt. Für die zwischen Agent und Client ausgetauschten Nachrichten definiert A2UI vier Nachrichtentypen:
| Nachrichtentyp | Beschreibung |
|---|---|
createSurface |
Weist den Client an, eine neue Surface zu erstellen. |
updateComponents |
Liefert eine Liste von Komponenten-Definitionen, die einer bestimmten Surface hinzugefügt oder dort aktualisiert werden. |
updateDataModel |
Liefert neue Daten, die in das Datenmodell einer Surface eingefügt werden oder dieses ersetzen. |
deleteSurface |
Entfernt eine Surface und deren Inhalte explizit aus der UI. |
Ein Surface ist eine logische Anzeigefläche im Client, die der Agent mit createSurface anlegt, mit updateComponents befüllt und bei Bedarf wieder mit deleteSurface entfernt. Die in den Komponenten genutzten Daten verwaltet A2UI in einem eigenen Datenmodell pro Surface, das sich über updateDataModel aktualisieren lässt. Komponenten greifen über Datenbindung auf die Werte dieses Datenmodells zu, sodass Änderungen automatisch in der Anzeige übernommen werden.
A2UI-Renderer in Angular einbinden
Damit Entwickler:innen das Protokoll nicht selbst implementieren müssen, bietet das A2UI-Projekt bereits Renderer für verschiedene Sprachen sowie Adapter für verschiedene Frameworks. Der Renderer für JavaScript findet sich im Paket @a2ui/web_core und der darauf aufbauende Adapter für Angular in @a2ui/angular. Wie gewohnt können beide Pakete über npm bezogen werden:
npm install @a2ui/angular @a2ui/web_core
Beispiel: Eine Passagier-Karte mit A2UI in Angular
Um die Funktionsweise von A2UI greifbar zu machen, beginnen wir mit einem minimalen Beispiel, das sich im Beispielprojekt unter projects/a2ui-demo befindet. Statt eines echten Sprachmodells erzeugt unser Beispielcode die A2UI-Nachrichten zunächst hartcodiert. So können wir uns voll auf das Datenformat und die clientseitige Verarbeitung konzentrieren.
Die Demo präsentiert eine Karte mit den Daten eines Passagiers sowie einen Button, mit dem sich die erworbenen Bonusmeilen erhöhen lassen:

Die Passagier-Karte wird mit der Funktion createSimpleCard definiert, die ein Array mit A2UI-Nachrichten erzeugt:
import type { A2uiMessage } from '@a2ui/web_core/v0_9';
export function createSimpleCard(
surfaceId: string,
catalogId: string,
passenger: Passenger,
): A2uiMessage[] {
return [
{
version: 'v0.9',
createSurface: {
surfaceId,
catalogId,
},
},
{
version: 'v0.9',
updateComponents: {
surfaceId,
components: [
{ id: 'root', component: 'Card', child: 'content' },
{
id: 'content',
component: 'Column',
children: ['headline', 'name-row', 'miles-row', 'button'],
},
{
id: 'headline',
component: 'Text',
text: 'Passenger',
variant: 'h2',
},
{
id: 'name-row',
component: 'Row',
children: ['first-name', 'last-name'],
},
{
id: 'first-name',
component: 'Text',
text: { path: '/passenger/firstName' },
variant: 'body',
},
{
id: 'last-name',
component: 'Text',
text: { path: '/passenger/lastName' },
variant: 'body',
},
{
id: 'miles-row',
component: 'Row',
children: ['miles-label', 'miles-value'],
},
{
id: 'miles-label',
component: 'Text',
text: 'Miles:',
variant: 'caption',
},
{
id: 'miles-value',
component: 'Text',
text: {
call: 'formatNumber',
args: {
value: { path: '/passenger/bonusMiles' },
decimals: 0,
},
returnType: 'string',
},
variant: 'body',
},
{
id: 'button',
component: 'Button',
child: 'button-label',
action: {
event: {
name: 'increaseMiles',
context: {
passenger: { path: '/passenger' },
},
},
},
},
{
id: 'button-label',
component: 'Text',
text: 'Increase Miles',
variant: 'body',
},
],
},
},
{
version: 'v0.9',
updateDataModel: {
surfaceId,
path: '/passenger',
value: passenger,
},
},
];
}
Die erste Nachricht vom Typ createSurface legt ein neues Surface an. Die zweite Nachricht beschreibt mittels updateComponents die anzuzeigenden Komponenten: eine Card als Wurzel, die eine Spalte mit Überschrift, Namensreihe, Meilenanzeige und Button enthält. Die einzelnen Ausgaben werden teilweise über die Eigenschaft path an Werte im Datenmodell gebunden. Dieses Datenmodell definiert das Beispiel in der dritten Nachricht, die den Typ updateDataModel aufweist.
Jede Komponente erhält eine eigene ID; geschachtelte Komponenten werden über child bzw. children anhand dieser IDs referenziert. Durch diese Art der Referenzierung befinden sich sämtliche Knoten des Komponentenbaums auf derselben Ebene – eine Schachtelung im JSON-Dokument umgeht A2UI damit ganz bewusst, da sie für Sprachmodelle eine Herausforderung darstellt.
Der Button beschreibt rein deklarativ, dass beim Klick ein Event increaseMiles mit den Passagierdaten als Kontext ausgelöst werden soll. Darauf zu reagieren ist die Aufgabe des Clients.
Zur Anzeige der beschriebenen Struktur nutzt das Beispiel den vom Angular-Adapter bereitgestellten A2uiRendererService:
import {
A2UI_RENDERER_CONFIG,
A2uiRendererService,
SurfaceComponent,
} from '@a2ui/angular/v0_9';
[...]
@Component({
selector: 'app-root',
imports: [SurfaceComponent],
template: `
<a2ui-v09-surface [surfaceId]="surfaceId" />
`,
styleUrl: './app.css',
})
export class App {
private readonly renderer = inject(A2uiRendererService);
protected readonly config = inject(A2UI_RENDERER_CONFIG);
private readonly destroyRef = inject(DestroyRef);
protected readonly surfaceId = 'passenger-card-surface';
constructor() {
this.render();
this.registerHandler();
}
private render(): void {
const passenger: Passenger = {
id: 42,
firstName: 'Anna',
lastName: 'Miller',
bonusMiles: 1200,
};
const messages = createSimpleCard(
this.surfaceId,
this.config.catalogs[0].id,
passenger,
);
this.renderer.processMessages(messages);
}
private registerHandler(): void {
[...]
}
}
Der A2uiRendererService verarbeitet die erhaltenen A2UI-Nachrichten mit seiner Methode processMessages. Die eigentliche Anzeige übernimmt die Komponente SurfaceComponent, die die gewünschte surfaceId entgegennimmt.
Neu: Agentic UI with Angular
Wenn du A2UI nicht nur integrieren, sondern sauber in größere Architekturen einbetten willst:
In meinem Buch Agentic UI mit Angular gehe ich genau auf diese Patterns und Trade-offs im Detail ein.
Da der Button in unserer Karte ein Event increaseMiles auslöst, registriert das Beispiel dafür einen Handler bei der Eigenschaft onAction:
import type { A2uiClientAction } from '@a2ui/web_core/v0_9';
[...]
private registerHandler(): void {
const subscription = this.renderer.surfaceGroup.onAction.subscribe(
(action: A2uiClientAction) => {
console.log('[A2UI Event]', action);
if (action.name !== 'increaseMiles') {
return;
}
const passenger = action.context['passenger'] as Passenger;
this.renderer.processMessages([
{
version: 'v0.9',
updateDataModel: {
surfaceId: this.surfaceId,
path: '/passenger',
value: {
...passenger,
bonusMiles: passenger.bonusMiles + 300,
},
},
},
]);
},
);
this.destroyRef.onDestroy(() => {
subscription.unsubscribe();
});
}
Der Handler für increaseMiles nimmt die im Kontext mitgegebenen Passagierdaten entgegen und aktualisiert die Bonusmeilen im gebundenen Datenmodell mit einer updateDataModel-Nachricht. Der Renderer erkennt die Änderung im Datenmodell und aktualisiert die gebundenen Anzeigen automatisch.
Leider handelt es sich bei onAction nicht um ein RxJS-Observable. Deswegen erfolgt hier das Unsubscribe programmatisch über eine DestroyRef.
Damit der Renderer weiß, welche Kataloge zur Verfügung stehen und wie etwaige Markdown-Inhalte umgewandelt werden sollen, sieht der Angular-Adapter eine zentrale Konfiguration vor, die über Provider bereitgestellt wird:
import {
A2UI_RENDERER_CONFIG,
A2uiRendererService,
BasicCatalog,
provideMarkdownRenderer,
} from '@a2ui/angular/v0_9';
import {
ApplicationConfig,
inject,
provideBrowserGlobalErrorListeners,
} from '@angular/core';
import { marked } from 'marked';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
{
provide: A2UI_RENDERER_CONFIG,
useFactory: () => ({
catalogs: [inject(BasicCatalog)],
}),
},
provideMarkdownRenderer(async (markdown) =>
marked.parse(String(markdown ?? '')),
),
A2uiRendererService,
],
};
Das gezeigte Beispiel konfiguriert den von A2UI mitgelieferten Basic Catalog, den der Adapter als Service bereitstellt. Dazu wird der Katalog für das Token A2UI_RENDERER_CONFIG registriert. Außerdem benötigt die vom A2UI-Team gelieferte Umsetzung des Basic-Kataloges einen Markdown-Renderer, der über provideMarkdownRenderer einzubinden ist.
Zusammenfassung
A2UI ermöglicht es einem Sprachmodell, auf Anfragen nicht nur mit Text, sondern mit konkreten UI-Strukturen zu antworten, die der Client zur Laufzeit direkt rendern kann. Das Modell kombiniert dabei vorhandene Komponenten zu passenden Oberflächen – abgestimmt auf den jeweiligen Kontext und die aktuelle Interaktion.
Ein zentraler Vorteil liegt in der Trennung von Struktur und Darstellung: Der Client entscheidet über die konkrete Darstellung und führt keinen fremden Code aus. Dadurch bleiben die dynamisch erzeugten UI-Fragmente konsistent mit der Anwendung und wirken nicht wie Fremdkörper.
Der nächste Schritt
Wir wissen jetzt, wie sich A2UI in Angular rendern lässt. Im nächsten Teil geht es darum, die UI von einem Agenten generieren zu lassen und über AG-UI anzubinden.
Interesse an produktionsreifen Agentic-UI-Architekturen?
In meinem Workshop beschäftigen wir uns mit AG-UI, A2UI, MCP Apps, HITL-Patterns und modernen Angular-Architekturen für reale agentische Systeme.
FAQ
Was ist A2UI?
A2UI (Agent-to-UI) ist ein von Google vorgestellter Standard, der die Lücke zwischen einem Agent und einer dynamisch generierten Oberfläche schließt. Er definiert ein Vokabular vordefinierter Komponenten samt Eigenschaften sowie ein Nachrichtenformat, mit dem ein Agent dem Client mitteilt, welche Komponenten anzuzeigen und mit welchen Daten sie zu befüllen sind.
Wofür braucht man A2UI?
A2UI ermöglicht es Sprachmodellen, auf Anfragen nicht nur mit Text, sondern mit konkreten UI-Strukturen wie Listen, Karten, Diagrammen oder Eingabeformularen zu antworten. Damit lassen sich agentische Sidecars und Assistenten weit über klassische Chatverläufe hinaus erweitern, ohne dass der Client zur Laufzeit fremden Code laden muss.
Was ist der Basic Catalog in A2UI?
Der Basic Catalog ist die von A2UI mitgelieferte Sammlung gängiger UI-Bausteine. Er umfasst Display-, Layout-, Container- und Input-Komponenten wie Text, Image, Card, Column, Row, Button oder TextField und enthält dazu Standardfunktionen wie formatNumber, formatDate oder typische Validierungen.
Wie integriert man A2UI in eine Angular-Anwendung?
Für Angular bietet A2UI das Paket @a2ui/angular, das auf dem JavaScript-Renderer @a2ui/web_core aufbaut. Der A2uiRendererService verarbeitet die A2UI-Nachrichten, die Komponente SurfaceComponent zeigt das gerenderte Surface an, und der gewünschte Katalog wird über das Token A2UI_RENDERER_CONFIG registriert.

