Skip to content

AI Insights

Building an AG-UI Compliant Frontend

A practical, interactive walkthrough for rendering AG-UI event streams in a real-world frontend.

Back to blog|December 21, 2025|2 min read
AG-UIProtocolsFrontendAgents

In Building an AG-UI Compliant Frontend, A practical, interactive walkthrough for rendering AG-UI event streams in a real-world frontend.

AG-UI (Agent–User Interaction) gives frontends a consistent way to render agent behavior without caring which model or backend framework sits behind it. Instead of asking “what model are you using?”, the UI focuses on the event stream: runs start, messages stream in, tools are invoked, and state updates arrive.

This post walks through a frontend-first approach. We will:

  • Mock the resources an agent would normally receive.
  • Replay event streams to validate the UI logic.
  • Finish with a full chat widget that simulates an AG-UI run end-to-end.

The frontend contract

AG-UI stays lightweight by reducing the contract to a handful of event sequences:

  • Runs: RUN_STARTEDRUN_FINISHED (or RUN_ERROR).
  • Messages: TEXT_MESSAGE_STARTTEXT_MESSAGE_CONTENT*TEXT_MESSAGE_END.
  • Tool calls: TOOL_CALL_STARTTOOL_CALL_ARGS*TOOL_CALL_END.
  • State: STATE_SNAPSHOT (full state) and STATE_DELTA (JSON Patch updates).
  • Resync: MESSAGES_SNAPSHOT for clean recovery.

In practice, the UI needs a reducer that can transform those events into:

  • A message timeline.
  • Tool call cards.
  • A state panel.
  • Run status indicators.

Interactive sample

AG-UI event reducer (minimal)

typescript
type ChatState = {
status: "idle" | "running" | "finished" | "error";
messages: { id: string; role: string; content: string }[];
toolCalls: { id: string; name: string; args: string }[];
agentState: Record<string, unknown> | null;
};

type StreamEvent =
| { type: "RUN_STARTED"; threadId: string; runId: string }
| { type: "RUN_FINISHED"; threadId: string; runId: string }
| { type: "TEXT_MESSAGE_START"; messageId: string; role: string }
| { type: "TEXT_MESSAGE_CONTENT"; messageId: string; delta: string }
| { type: "TEXT_MESSAGE_END"; messageId: string }
| { type: "TOOL_CALL_START"; toolCallId: string; toolCallName: string; parentMessageId?: string }
| { type: "TOOL_CALL_ARGS"; toolCallId: string; delta: string }
| { type: "TOOL_CALL_END"; toolCallId: string }
| { type: "STATE_SNAPSHOT"; snapshot: Record<string, unknown> }
| { type: "STATE_DELTA"; delta: { op: string; path: string; value?: unknown }[] }
| { type: "MESSAGES_SNAPSHOT"; messages: { id: string; role: string; content: string }[] };

const applyEvent = (state: ChatState, event: StreamEvent): ChatState => {
switch (event.type) {
  case "RUN_STARTED":
    return { ...state, status: "running" };
  case "RUN_FINISHED":
    return { ...state, status: "finished" };
  case "TEXT_MESSAGE_START":
    return {
      ...state,
      messages: [...state.messages, { id: event.messageId, role: event.role, content: "" }],
    };
  case "TEXT_MESSAGE_CONTENT":
    return {
      ...state,
      messages: state.messages.map(message =>
        message.id === event.messageId
          ? { ...message, content: message.content + event.delta }
          : message
      ),
    };
  case "STATE_SNAPSHOT":
    return { ...state, agentState: event.snapshot };
  default:
    return state;
}
};

Mock the resources

In production, your agent might receive tools, context, and initial state from a backend (for example, a pydantic-ai service). In a demo environment, you can still model those payloads, keep them local, and reuse them for UI development and demos.

Interactive sample

Mock the agent resources

JSON
{
  "agent": {
    "id": "ag-ui-demo",
    "threadId": "thread-demo-001",
    "description": "AG-UI teaching agent for frontend teams."
  },
  "tools": [
    {
      "name": "summarize_guidelines",
      "description": "Summarize AG-UI contract details for the active UI view.",
      "parameters": {
        "type": "object",
        "properties": {
          "topic": {
            "type": "string"
          },
          "audience": {
            "type": "string",
            "enum": [
              "product",
              "engineering",
              "design"
            ]
          }
        },
        "required": [
          "topic"
        ]
      }
    },
    {
      "name": "draft_component_spec",
      "description": "Generate a component checklist for an AG-UI widget.",
      "parameters": {
        "type": "object",
        "properties": {
          "surface": {
            "type": "string"
          },
          "constraints": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "surface"
        ]
      }
    }
  ],
  "context": [
    {
      "description": "Audience",
      "value": "Frontend teams shipping agent-native workflows."
    },
    {
      "description": "Experience goal",
      "value": "Make event semantics visible while keeping the UI calm."
    }
  ],
  "state": {
    "tone": "crisp",
    "draftStatus": "idle",
    "lastRunAt": null,
    "compliance": {
      "events": "required",
      "tools": "optional",
      "state": "supported"
    }
  }
}

Agent

IDag-ui-demo
Threadthread-demo-001

AG-UI teaching agent for frontend teams.

Tools

summarize_guidelines

Summarize AG-UI contract details for the active UI view.

ParametersShow JSON
{
  "type": "object",
  "properties": {
    "topic": {
      "type": "string"
    },
    "audience": {
      "type": "string",
      "enum": [
        "product",
        "engineering",
        "design"
      ]
    }
  },
  "required": [
    "topic"
  ]
}
draft_component_spec

Generate a component checklist for an AG-UI widget.

ParametersShow JSON
{
  "type": "object",
  "properties": {
    "surface": {
      "type": "string"
    },
    "constraints": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "surface"
  ]
}

Context

Audience

Frontend teams shipping agent-native workflows.

Experience goal

Make event semantics visible while keeping the UI calm.

Agent stateShow JSON
{
  "tone": "crisp",
  "draftStatus": "idle",
  "lastRunAt": null,
  "compliance": {
    "events": "required",
    "tools": "optional",
    "state": "supported"
  }
}

This mirrors a `RunAgentInput` payload. Keep it structured and consistent.

Replay the event stream

Once you have resources defined, the frontend just needs to react to events. Editing the event array below instantly changes the preview so you can validate tool call rendering, state updates, and message timing.

Interactive sample

Stream AG-UI events into UI

Events
[
  {
    "type": "RUN_STARTED",
    "threadId": "thread-demo-001",
    "runId": "run-101"
  },
  {
    "type": "STATE_SNAPSHOT",
    "snapshot": {
      "phase": "thinking",
      "activeGoal": "Define the frontend event contract",
      "compliance": {
        "events": true,
        "tools": true,
        "state": true
      }
    }
  },
  {
    "type": "TEXT_MESSAGE_START",
    "messageId": "m1",
    "role": "user"
  },
  {
    "type": "TEXT_MESSAGE_CONTENT",
    "messageId": "m1",
    "delta": "Show me how to render an AG-UI compliant chat timeline."
  },
  {
    "type": "TEXT_MESSAGE_END",
    "messageId": "m1"
  },
  {
    "type": "TEXT_MESSAGE_START",
    "messageId": "m2",
    "role": "assistant"
  },
  {
    "type": "TEXT_MESSAGE_CONTENT",
    "messageId": "m2",
    "delta": "We will stream text events, display tool calls inline, and keep state snapshots visible for debugging."
  },
  {
    "type": "TEXT_MESSAGE_END",
    "messageId": "m2"
  },
  {
    "type": "TOOL_CALL_START",
    "toolCallId": "tool-1",
    "toolCallName": "draft_component_spec",
    "parentMessageId": "m2"
  },
  {
    "type": "TOOL_CALL_ARGS",
    "toolCallId": "tool-1",
    "delta": "{\"surface\":\"chat-widget\",\"constraints\":[\"frontend\",\"event-driven\"]}"
  },
  {
    "type": "TOOL_CALL_END",
    "toolCallId": "tool-1"
  },
  {
    "type": "TEXT_MESSAGE_START",
    "messageId": "m3",
    "role": "tool"
  },
  {
    "type": "TEXT_MESSAGE_CONTENT",
    "messageId": "m3",
    "delta": "Checklist: message stream, tool rail, state panel, run controls."
  },
  {
    "type": "TEXT_MESSAGE_END",
    "messageId": "m3"
  },
  {
    "type": "STATE_DELTA",
    "delta": [
      {
        "op": "replace",
        "path": "/phase",
        "value": "ready"
      },
      {
        "op": "add",
        "path": "/lastRunAt",
        "value": "2025-12-21T18:15:00Z"
      }
    ]
  },
  {
    "type": "RUN_FINISHED",
    "threadId": "thread-demo-001",
    "runId": "run-101"
  }
]

AG-UI Chat Timeline

Event-driven UI driven by agent events.

Complete
user
Show me how to render an AG-UI compliant chat timeline.
assistant
We will stream text events, display tool calls inline, and keep state snapshots visible for debugging.
draft_component_speccomplete
ArgsShow JSON
{"surface":"chat-widget","constraints":["frontend","event-driven"]}
tool
Checklist: message stream, tool rail, state panel, run controls.
Agent stateShow JSON
{
  "phase": "ready",
  "activeGoal": "Define the frontend event contract",
  "compliance": {
    "events": true,
    "tools": true,
    "state": true
  },
  "lastRunAt": "2025-12-21T18:15:00Z"
}

Edit the event array and replay to see how a compliant frontend reconstructs messages, tool calls, and state.

Build the final chat widget

The widget below puts everything together: a chat timeline, tool rail, state panel, and run controls. The event flow mirrors what you would receive from a real AG-UI backend.

AG-UI compliant widget

Interactive chat surface with agent resources

This widget simulates an event stream that could come from pydantic-ai or any AG-UI backend.

AG-UI Chat Timeline

Event-driven UI driven by agent events.

Idle
No messages yet. Stream an event sequence to populate the timeline.

Agent resources

Tools2
Context items2

Tools

summarize_guidelines

Summarize AG-UI contract details for the active UI view.

ParametersShow JSON
{
  "type": "object",
  "properties": {
    "topic": {
      "type": "string"
    },
    "audience": {
      "type": "string",
      "enum": [
        "product",
        "engineering",
        "design"
      ]
    }
  },
  "required": [
    "topic"
  ]
}
draft_component_spec

Generate a component checklist for an AG-UI widget.

ParametersShow JSON
{
  "type": "object",
  "properties": {
    "surface": {
      "type": "string"
    },
    "constraints": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "surface"
  ]
}

Context

Audience

Frontend teams shipping agent-native workflows.

Experience goal

Make event semantics visible while keeping the UI calm.

Agent stateShow JSON
{
  "tone": "crisp",
  "draftStatus": "idle",
  "lastRunAt": null,
  "compliance": {
    "events": "required",
    "tools": "optional",
    "state": "supported"
  }
}
Last run eventsShow JSON
[
  {
    "type": "RUN_STARTED",
    "threadId": "thread-demo-002",
    "runId": "run-201"
  },
  {
    "type": "STATE_SNAPSHOT",
    "snapshot": {
      "phase": "thinking",
      "lastUserMessage": "Hello",
      "ui": {
        "hint": "Streamed response + tool rail"
      }
    }
  },
  {
    "type": "TEXT_MESSAGE_START",
    "messageId": "user-1",
    "role": "user"
  },
  {
    "type": "TEXT_MESSAGE_CONTENT",
    "messageId": "user-1",
    "delta": "Hello"
  },
  {
    "type": "TEXT_MESSAGE_END",
    "messageId": "user-1"
  },
  {
    "type": "TEXT_MESSAGE_START",
    "messageId": "assistant-1",
    "role": "assistant"
  }
]