Implementing AG-UI with Angular

  1. Understanding AG-UI: The Standard for Agentic User Interfaces
  2. AG-UI in Practice: The SDK for TypeScript
  3. Implementing AG-UI with Angular

Using agents via AG-UI in Angular applications

AG-UI defines communication between frontend and agent and its SDK provides a low-level implementation. However, for Angular applications it does not provide a fully idiomatic abstraction. If you want to integrate AI agents into Angular, you need a wrapper that fits naturally into components, signals, and templates.

This is exactly where this article comes in. It shows how AG-UI can be used with a lean Angular-specific abstraction on top of the SDK so that agents, client-side tools, and widgets can be integrated cleanly into an Angular application.

📂 Source Code (see branch agentic)

Integrating AG-UI into Angular

The AG-UI SDK discussed earlier provides a solid foundation for integration in Angular. However, to enable convenient use without unnecessary boilerplate code, an additional abstraction layer is needed. The accompanying demo project contains such an abstraction in the libs/ag-ui-client folder. To simplify imports, the tsconfig.json includes a path mapping called @internal/ag-ui-client that points to it.

The centerpiece of this implementation is an agUiResource used to communicate with the agent. It also provides a WidgetContainer component that displays components requested by the agent, such as the flight card shown earlier. Its API design is heavily inspired by the Hashbrown framework, which offers Angular developers a highly idiomatic way of working with LLMs, but currently does not yet include AG-UI integration.

This article treats that abstraction as a black box that should sooner or later be provided by a library. The source code can serve as a starting point for your own work.

Thanks to AG-UI, the Angular example is independent of server-side technologies and models. To keep execution as simple as possible, the demo application includes an agent built with the extremely convenient TypeScript-based agent framework Mastra. Since the AG-UI SDK provides an adapter for Mastra, agents built with it can easily be connected through AG-UI.

The client was tested with both OpenAI's GPT 5 and Google's Gemini 3. Details on how to set up and start the demo can be found in the README.

Connecting Agents via agUiResource in Angular

The agUiResource provided by @internal/ag-ui-client implements Angular's Resource API and can therefore be used in a similar way to the familiar httpResource or rxResource:

import { agUiResource } from '@internal/ag-ui-client';

[...]

export class TicketingComponent {
  private readonly chat = agUiResource({
    url: 'http://localhost:3001/ag-ui/ticketingAgent',
    useServerMemory: true,
    tools: [
      findFlightsTool,
      getLoadedFlightsTool,
      toggleFlightSelectionTool,
      getCurrentBasketTool,
      displayFlightDetailTool,
      createShowComponentsTool([flightWidgetComponent]),
    ],
  });
}

The properties refer to the URL of the agent. With useServerMemory, the caller indicates whether the agent stores the chat history. If it does not, the client must repeat the entire chat history with every request.

In the tools list, the consumer registers all client-side tools that the agent is allowed to request. One special tool is showComponents, which is created via a factory. This factory accepts the descriptions of possible components that the agent may embed when answering questions. In our case, only a flight card (flightWidget) is available.

NOTE

Agentic UI with Angular

Learn more about this topic in my eBook: Build scalable agentic UIs with Angular — using AG-UI, A2UI, and MCP Apps, open and free from vendor lock-in.

Cover of the eBook Agentic UI with Angular

Learn more about the eBook →

The individual tools can be described using the defineAgUiTool function:

import { defineAgUiTool } from '@internal/ag-ui-client';
import { z } from 'zod';

[...]

export const findFlightsTool = defineAgUiTool({
  name: 'findFlights',
  description: `Searches for flights and redirects the user to the result`,
  schema: z.object({
    from: z.string().describe('airport of departure'),
    to: z.string().describe('airport of destination'),
  }),
  execute: async (args) => {
    const store = inject(FlightStore);
    const router = inject(Router);
    store.updateFilter(args.from, args.to);
    await router.navigate(['/ticketing/booking/flight-search']);
  },
});

Besides a name, a description, and parameter definitions based on Zod, the tool definition also includes an execute method. When the agent requests a tool, agUiResource executes that method.

The args parameter object conforms to the TypeScript type inferred from the Zod schema. In our case, that is an object with the properties from and to. The implementation shown triggers a flight search by passing these search criteria to the FlightStore and then navigates the user to the results page.

Tools that gather data, such as local state or user responses, can report that data back to the model via their return value. One example is getLoadedFlightsTool, which informs the agent about the flights currently displayed to the user by the application:

export const getLoadedFlightsTool = defineAgUiTool({
  name: 'getLoadedFlights',
  description: `Returns the currently loaded/displayed flights`,
  execute: () => {
    const store = inject(FlightStore);
    return store.flightsValue().map(toFlightInfo);
  },
});

Defining components works similarly, but here the schema describes the offered inputs:

import { defineAgUiComponent } from '@internal/ag-ui-client';
import { z } from 'zod';

export const flightWidgetComponent = defineAgUiComponent({
  name: 'flightWidget',
  description: `Displays a concrete flight as an interactive card.
Use it when referring to one or more specific flights.`,
  component: FlightWidget,
  schema: z.object({
    flight: flightSchema,
    status: z.enum(['booked', 'other']).describe('Status of the flight'),
  }),
});

In this example, the flight property refers to another Zod schema:

const flightSchema = z.object({
  id: z.number().describe('The flight id'),
  from: z.string().describe('Departure city'),
  to: z.string().describe('Arrival city'),
  date: z.string().describe('Departure date in ISO format'),
  delay: z.number().describe('Delay in minutes'),
});

To send a request to the agent, the client uses the sendMessage method:

this.chat.sendMessage({
  role: 'user',
  content: 'Did I book my flight to France?',
});

At that point, the client only needs to display the response messages returned by the agent via AG-UI in the chat history.

Presenting the Chat History in Angular Templates

The entire chat history is stored in the value of AgUiResource. It is a signal containing an array of AgUiChatMessages:

@for (message of chat.value(); track message.id) {
  @if (message.content) {
    <div>{{ message.content }}</div>
  }
  @for (widget of message.widgets; track widget.id) {
    <app-widget-container [widget]="widget" />
  }
  @for (toolCall of message.toolCalls; track toolCall.id) {
    <div>Tool Call: {{ toolCall.name }}</div>
  }
}

In addition to a textual response (content), the iterated messages also contain a list of components (widgets) and tools (toolCalls). The widgets are components registered for the showComponents tool and selected by the LLM. The WidgetContainer offered by @internal/ag-ui-client can create these widgets dynamically and render them using the properties passed by the agent or the LLM.

The toolCalls list provides information about the requested tools. Its entries can refer to both client-side and server-side tools and are displayed here for reasons of transparency. We do not need to take care of executing client-side tools ourselves, however, because agUiResource handles that task automatically.

The streamed AG-UI messages can be inspected through the browser's developer tools:

Streamed AG-UI messages in the browser's developer tools

On the one hand, this makes the protocol tangible; on the other, it helps with troubleshooting.

Summary

With a lean Angular abstraction such as agUiResource, AG-UI can be integrated idiomatically into components, tools, and templates. The client describes the available tools and widgets, sends user requests to the agent, and processes streamed responses directly as a signal-based chat history.

FAQ

How Can AG-UI Be Integrated into Angular?

AG-UI is best integrated into Angular through a dedicated abstraction that encapsulates communication with the agent and fits cleanly into components, signals, and templates. In the example shown here, that role is fulfilled by an agUiResource.

What Is agUiResource?

agUiResource is an Angular-specific wrapper around the AG-UI SDK. It connects the agent to the Angular application, executes client-side tools, and exposes the chat history together with widgets and tool calls as a resource. The entire application can be found in the linked project.

Why Does Angular Need Another Abstraction on Top of the SDK?

The AG-UI SDK is intentionally generic and low-level. An Angular-specific abstraction reduces boilerplate, aligns better with the Resource API, and makes integration into templates, stores, and routing easier.

How Do Tools and Widgets End Up in the Chat History?

Before a run starts, the client registers the available tools and components with the agent. When the agent later requests tool calls or widgets, agUiResource processes that information and exposes it to the template through the signal-based chat history.

Agentic UI with Angular

Architecting Agentic AI with Open Standards

Integrate AI Agents in Angular with Open Standards.

More About the Book