Skip to content
Back to the Lab

Building Agentic Frontend Interfaces with AG UI Protocol

Learn how to build agentic AI interfaces in React using the AG-UI protocol. Covers event-driven agent state, generative UI component registries, security patterns for agent-rendered content, and handling long-running agent tasks.

Building Agentic Frontend Interfaces with AG UI Protocol

Introduction#

The first generation of AI chat interfaces followed a simple pattern: the user types a message, the model streams back text, and the conversation continues. This worked well for question answering, but it falls apart the moment an AI agent needs to do real work, things like searching a database across several steps, calling multiple tools in sequence, or waiting on a long running task. A plain text stream cannot represent “the agent is currently querying three data sources and will summarize once they return.”

This gap led to what the ecosystem now calls the “chat wall” problem. Conversational AI is powerful, but a pure text interface is a poor way to surface structured intermediate state, request specific input like a date range or a dropdown selection, or display results that are genuinely better shown as a table or chart than as prose.

Agentic frontend interfaces are the architectural response. Rather than treating the AI as a black box that returns text, modern AI native applications treat the agent’s reasoning process, tool calls, and outputs as first class UI state. This guide covers the protocols and patterns that make this possible, with a focus on the AG-UI protocol and the generative UI techniques built on top of it.

The AG UI Protocol#

AG UI is an open, event based protocol designed specifically to standardize how an AI agent communicates with the frontend interface that displays its work. Where the Model Context Protocol governs how an agent talks to its tools and data sources, AG UI governs how an agent talks to the screen the user is looking at. The protocol has been adopted across Google, AWS, Microsoft, and LangChain, with the explicit goal of decoupling the agent’s underlying framework from the frontend rendering it.

The core idea is a stream of typed events that the frontend subscribes to and reacts to in real time. Rather than a single opaque text response, the agent emits a sequence of structured events as it works:

// Simplified representation of an AG-UI event stream
{ type: "RUN_STARTED", runId: "run_123" }
{ type: "TEXT_MESSAGE_CONTENT", content: "Let me check your order history..." }
{ type: "TOOL_CALL_START", toolName: "search_orders", toolCallId: "tc_1" }
{ type: "TOOL_CALL_END", toolCallId: "tc_1", result: { orders: [...] } }
{ type: "STATE_DELTA", patch: [{ op: "add", path: "/orders", value: [...] }] }
{ type: "TEXT_MESSAGE_CONTENT", content: "I found 3 recent orders." }
{ type: "RUN_FINISHED", runId: "run_123" }

Because the protocol is framework agnostic, an interface built against AG UI events does not need to change when the underlying agent framework changes from LangGraph to a custom orchestration layer, or from one model provider to another. The frontend reacts to the event shape, not the implementation behind it.

Building a React Component on AG UI Events#

The practical implementation pattern is a hook that subscribes to the event stream and maintains derived UI state, similar in spirit to how you would consume a WebSocket or Server Sent Events connection:

function useAgentRun(runConfig: AgentRunConfig) {
  const [messages, setMessages] = useState<Message[]>([]);
  const [activeToolCalls, setActiveToolCalls] = useState<ToolCall[]>([]);
  const [agentState, setAgentState] = useState<Record<string, unknown>>({});

  useEffect(() => {
    const eventSource = connectToAgUiStream(runConfig);

    eventSource.on("TEXT_MESSAGE_CONTENT", (event) => {
      setMessages((prev) => appendOrUpdateMessage(prev, event));
    });

    eventSource.on("TOOL_CALL_START", (event) => {
      setActiveToolCalls((prev) => [...prev, { id: event.toolCallId, status: "running" }]);
    });

    eventSource.on("TOOL_CALL_END", (event) => {
      setActiveToolCalls((prev) =>
        prev.map((tc) =>
          tc.id === event.toolCallId ? { ...tc, status: "done", result: event.result } : tc
        )
      );
    });

    eventSource.on("STATE_DELTA", (event) => {
      setAgentState((prev) => applyJsonPatch(prev, event.patch));
    });

    return () => eventSource.close();
  }, [runConfig]);

  return { messages, activeToolCalls, agentState };
}

This hook gives a component everything it needs to render a rich, live view of an agent’s work in progress: the conversational text, a list of tool calls with their live status, and any shared state the agent is building up, such as a running list of search results.

Generative UI Beyond Text#

Generative UI is the layer built on top of agent event protocols that allows the agent to render actual interface components, not just text, as part of its response. Instead of describing a list of search results in prose, the agent can request that the frontend render a results table component, passing it structured data.

The architecture typically involves a tool registry that maps tool calls to specific UI components:

const generativeUIRegistry: Record<string, React.ComponentType<any>> = {
  display_flight_options: FlightOptionsCard,
  show_calendar_picker: CalendarPickerWidget,
  render_comparison_table: ComparisonTable,
  confirm_booking: BookingConfirmationForm,
};

function AgentMessage({ toolCall }: { toolCall: ToolCall }) {
  const Component = generativeUIRegistry[toolCall.toolName];

  if (!Component) {
    return <GenericResultDisplay data={toolCall.result} />;
  }

  return <Component {...toolCall.result} onAction={toolCall.respond} />;
}

The critical addition compared to a static UI is the onAction callback. When the agent renders a calendar picker or a confirmation form, the user’s interaction with that component needs to flow back to the agent as a structured response, continuing the conversation with real input rather than the user typing free text that has to be re parsed.

Security: Decoupling Structure from Implementation#

A foundational design principle across modern agent UI protocols, including AG UI and the related A2UI specification, is the strict separation between the UI structure the agent requests and the UI implementation that actually renders it. The agent does not send raw HTML or executable code to the frontend. It sends a declarative description, typically JSON, describing what kind of component to render and what data to pass it.

This separation exists specifically to prevent prompt injection from translating into arbitrary code execution or UI manipulation. If an agent’s output were directly rendered as HTML or JavaScript, a malicious or compromised data source feeding into the agent’s context could inject executable content into the user’s browser. By constraining the agent to requesting components from a fixed, developer defined registry, the worst case is a rendering error or fallback display, not a security breach.

function AgentMessage({ toolCall }: { toolCall: ToolCall }) {
  const Component = generativeUIRegistry[toolCall.toolName];

  // Unknown or unregistered tool names never reach arbitrary rendering logic
  if (!Component) {
    return <GenericResultDisplay data={toolCall.result} />;
  }

  // Props are validated against a schema before being passed to the component
  const validatedProps = validateAgainstSchema(toolCall.result, Component.propsSchema);
  return <Component {...validatedProps} />;
}

Validating the data passed into each generative component against a defined schema, rather than trusting the agent’s output blindly, is the practical implementation of this principle. Treat every payload coming from the agent as you would treat untrusted user input.

Handling Long Running and Asynchronous Agent Work#

Agentic workflows frequently involve steps that take longer than a typical request response cycle, multi minute research tasks, background data processing, or workflows that wait on external systems. Agent UI protocols handle this by treating the run itself as a long lived, resumable entity rather than a single request.

The frontend pattern that supports this is a persistent connection or polling mechanism tied to a run ID, allowing the user to navigate away and return to find the agent’s progress preserved:

function useResumableAgentRun(runId: string) {
  const [status, setStatus] = useState<"running" | "completed" | "failed">("running");

  useEffect(() => {
    const reconnect = () => {
      const stream = reconnectToRun(runId);
      stream.on("RUN_FINISHED", () => setStatus("completed"));
      stream.on("RUN_ERROR", () => setStatus("failed"));
      return stream;
    };

    const stream = reconnect();
    return () => stream.close();
  }, [runId]);

  return status;
}

Surfacing this state clearly, distinguishing “the agent is actively working,” “the agent is waiting on your input,” and “the agent has finished” prevents the most common source of user confusion in agentic interfaces: not knowing whether anything is happening at all.

Choosing a Framework#

Several frameworks have emerged to implement these patterns without building the protocol handling from scratch. CopilotKit, built on the AG UI protocol, has gained significant adoption with over 31,000 GitHub stars and works across multiple agent frameworks while keeping the frontend implementation stable. Tambo focuses specifically on the generative UI layer for React applications. The Vercel AI SDK remains the dominant choice for teams already in the Next.js ecosystem, with strong support for streaming and tool calling, though it is less focused on the structured agent state and generative UI patterns that AG UI native tools provide.

The right choice depends on how agentic your feature genuinely is. A straightforward chatbot with simple tool calls is well served by the Vercel AI SDK alone. A feature where the user needs to see an agent’s multi step reasoning, interact with intermediate UI elements, and resume long running tasks benefits from a framework built specifically around an agent UI protocol like AG UI.

Conclusion#

The shift from chat interfaces to agentic interfaces is, at its core, a shift from treating AI output as a string to treating it as structured, typed events that drive real UI state. Protocols like AG UI exist to standardize that event vocabulary so frontend teams are not reinventing it for every new agent framework that emerges.

For frontend developers, this means the skills that matter are shifting too. Understanding event driven state management, building component registries that can safely render agent requested UI, and validating untrusted structured data are becoming as central to AI feature development as understanding how to call a completions endpoint.

References#

Open Source Toolkit for Building AI Agents

The A2UI Protocol A Complete Guide to Agent-Driven Interfaces

Thesys React SDK Turn LLM Responses into Real Time User Interfaces

Top AI Agent Protocols to Know

Open Source AI Agents for React Apps