{"id":33203,"date":"2026-04-20T14:57:18","date_gmt":"2026-04-20T12:57:18","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=33203"},"modified":"2026-07-24T09:52:27","modified_gmt":"2026-07-24T07:52:27","slug":"implementing-ag-ui-with-angular","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/","title":{"rendered":"Agentic UI with Angular, CopilotKit, and AG-UI"},"content":{"rendered":"<div class=\"wp-post-series-box series-agentic-angular wp-post-series-box--expandable\">\n\t\t\t<input id=\"collapsible-series-agentic-angular6a66200ea7344\" class=\"wp-post-series-box__toggle_checkbox\" type=\"checkbox\">\n\t\n\t<label\n\t\tclass=\"wp-post-series-box__label\"\n\t\t\t\t\tfor=\"collapsible-series-agentic-angular6a66200ea7344\"\n\t\t\ttabindex=\"0\"\n\t\t\t\t>\n\t\t<p class=\"wp-post-series-box__name wp-post-series-name\">\n\t\t\tThis is post 4 of 9 in the series <em>&ldquo;Agentic Angular&rdquo;<\/em>\t\t<\/p>\n\t\t\t<\/label>\n\n\t\t\t<div class=\"wp-post-series-box__posts\">\n\t\t\t<ol>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/understanding-ag-ui-the-standard-for-agentic-user-interfaces\/\">Understanding AG-UI: The Standard for Agentic User Interfaces<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/ag-ui-in-practice-the-sdk-for-typescript\/\">AG-UI in Practice: The SDK for TypeScript<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/ag-ui-in-practice-ag-ui-end-to-end-connecting-server-and-client\/\">AG-UI End to End: Connecting Server and Client<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Agentic UI with Angular, CopilotKit, and AG-UI<\/span><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/a2ui-how-ai-generates-dynamic-uis-at-runtime\/\">A2UI: How AI Generates Dynamic UIs at Runtime<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/integrating-a2ui-with-ag-ui-in-angular\/\">Integrating A2UI with AG-UI and CopilotKit in Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\">Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/how-i-made-my-a2ui-dashboard-300-times-faster\/\">A2UI with a DSL: Controllable Dashboards Optimized for Performance<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/architecture-as-a-guardrail-structurally-ruling-out-entire-classes-of-errors\/\">Architecture as a Guardrail: Structurally Ruling Out Entire Classes of Errors<\/a><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n<p><em>Using agents via AG-UI and CopilotKit in Angular applications<\/em><\/p>\n<p>AG-UI defines the communication between frontend and agent. With <code>@copilotkit\/angular<\/code>, an Angular integration is now available too. It comes from the team behind CopilotKit, the same environment AG-UI itself originated from.<\/p>\n<p>This is exactly where this article comes in. Whereas the previous part reached a result quickly with the ready-made chat control, from here on we use CopilotKit's flexible headless mode: it provides the state and behavior of the chat but leaves the presentation entirely to the application. On that basis, the article shows how agents can be connected to an Angular application with CopilotKit, how client-side tools and widgets are defined, and where a small self-written helper function is still needed today.<\/p>\n<p>\ud83d\udcc2 <a href=\"https:\/\/github.com\/angular-architects\/flights42\/tree\/copilotkit\">Source Code<\/a> (see branch <code>copilotkit<\/code>)<\/p>\n<h2>The Example Application<\/h2>\n<p>As a running example throughout the series, we use a flight-booking application. It works perfectly fine in the conventional way; but when users get stuck or want to shorten individual work steps, they activate a <em>sidecar<\/em>: a chat through which a server-side agent helps out. It answers questions with text, calls client- and server-side tools, and renders interactive components such as a flight card into the chat.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/solution.png\" alt=\"Sidecar in a flight-booking application with chat, tool calls, and a flight card\" \/><\/p>\n<h2>Integrating CopilotKit into Angular<\/h2>\n<p>CopilotKit builds directly on the AG-UI SDK discussed earlier: on the client side, the agent is represented by an <code>HttpAgent<\/code> from <code>@ag-ui\/client<\/code>, which CopilotKit manages and whose streamed AG-UI events it translates into a signal-based chat history. The server-side implementation remains untouched by this \u2013 if you already have an AG-UI endpoint, you can keep using it unchanged.<\/p>\n<p>Wiring it into the application happens as usual through a provider in <code>app.config.ts<\/code>:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/app.config.ts\nimport { provideCopilotKit } from &#039;@copilotkit\/angular&#039;;\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    [...]\n    provideCopilotKit({}),\n  ],\n};<\/code><\/pre>\n<p>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 <a href=\"https:\/\/mastra.ai\/\">Mastra<\/a>. Since the AG-UI SDK provides an adapter for <code>Mastra<\/code>, agents built with it can easily be connected through AG-UI.<\/p>\n<p>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.<\/p>\n<h2>Connecting Agents through an Agent Store<\/h2>\n<p>The centerpiece of CopilotKit's Angular integration is the so-called agent store. It represents a concrete agent together with its chat history and execution state. The demo project encapsulates the setup of the ticketing agent in a function called <code>injectTicketingAgentStore<\/code>:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/ticketing\/ai\/ticketing-agent-store.ts\nimport { injectAgentStore } from &#039;@copilotkit\/angular&#039;;\nimport { initAgentStore } from &#039;..\/..\/shared\/util-copilotkit\/init-agent-store&#039;;\n\n[...]\n\nconst AGENT_ID = &#039;ticketingAgent&#039;;\n\nexport function injectTicketingAgentStore() {\n  initAgentStore({\n    agentId: AGENT_ID,\n    url: &#039;http:\/\/localhost:3001\/ag-ui\/ticketingAgent&#039;,\n    useServerMemory: true,\n    frontendTools: [\n      findFlightsTool,\n      getLoadedFlightsTool,\n      toggleFlightSelectionTool,\n      getCurrentBasketTool,\n      displayFlightDetailTool,\n      flightWidget,\n    ],\n  });\n\n  return injectAgentStore(AGENT_ID);\n}<\/code><\/pre>\n<p>The helper function <code>initAgentStore<\/code> registers the agent together with its client-side tools with CopilotKit; its structure is discussed in the next section. After that, <code>injectAgentStore<\/code> \u2013 provided by CopilotKit \u2013 returns the actual agent store: a <code>Signal&lt;AgentStore&gt;<\/code> that exposes, among other things, the chat history (<code>messages<\/code>) and the execution state (<code>isRunning<\/code>) as signals.<\/p>\n<p>The configuration refers to the URL of the agent. Behind it sits a server-side <a href=\"https:\/\/github.com\/angular-architects\/flights42\/blob\/copilotkit\/ai-server\/src\/mastra\/agents\/ticketing-agent.ts\">ticketing agent<\/a> that supports travelers with their flight bookings. For this, it has tools for looking up booked flights as well as for booking and cancelling them (<code>findBookedFlights<\/code>, <code>bookFlight<\/code>, <code>cancelFlight<\/code>). In our case it is implemented with the TypeScript framework Mastra; for the integration shown here, however, that does not matter \u2013 all that counts is that the agent supports AG-UI.<\/p>\n<p>With <code>useServerMemory<\/code>, 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.<\/p>\n<p>In the <code>frontendTools<\/code> list, the consumer registers all client-side tools that the agent is allowed to request. Interestingly, this list also includes the widget <code>flightWidget<\/code> \u2013 the flight card shown at the beginning. That is no coincidence: in CopilotKit, widgets are nothing but frontend tools that have an Angular component assigned for their presentation.<\/p>\n<p><div style=\"\nmargin: 8px 0;\npadding: 22px;\nborder: 1px solid #e5e7eb;\nborder-radius: 14px;\nbackground: #f8fafc;\n\">NOTE<\/p>\n<h3 style=\"margin-top:0\">Agentic UI with Angular<\/h3>\n<p>If you don\u2019t just want to integrate AG-UI but embed it into a scalable architecture:<br \/>\nIn my book Agentic UI with Angular, I cover exactly these patterns and trade-offs in depth.<\/p>\n<p><a href=\"https:\/\/agentic-angular.com\/\"><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/cover.png\" width=\"400\" alt=\"Cover of the eBook Agentic UI with Angular\" style=\"cursor:pointer !important\"><\/a><\/p>\n<p><a style=\"cursor:pointer !important\" href=\"https:\/\/agentic-angular.com\/\">Learn more about the eBook \u2192<\/a>\n<\/div>\n<\/p>\n<h2>The Helper Function <code>initAgentStore<\/code><\/h2>\n<p>Currently, CopilotKit expects agents to be configured \u2013 and thus registered \u2013 when <code>provideCopilotKit<\/code> is called during application startup. For lazy-loaded feature areas, that is impractical: their tools and widgets should only reach the browser together with the respective bundle instead of ending up in the main bundle.<\/p>\n<p>This is exactly the gap <code>initAgentStore<\/code> closes. It runs in an injection context and essentially does two things: it registers the agent at runtime as a so-called self-managed agent with CopilotKit and then registers all frontend tools for its <code>agentId<\/code> (slightly shortened):<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/shared\/util-copilotkit\/init-agent-store.ts\nimport { randomUUID } from &#039;@ag-ui\/client&#039;;\nimport { inject } from &#039;@angular\/core&#039;;\nimport { CopilotKit, registerFrontendTool } from &#039;@copilotkit\/angular&#039;;\n\n[...]\n\nexport function initAgentStore(config: InitAgentStoreConfig): void {\n  const copilotKit = inject(CopilotKit);\n\n  const httpAgent = new AppHttpAgent(\n    {\n      agentId: config.agentId,\n      url: config.url,\n      threadId: randomUUID(),\n    },\n    { useServerMemory: config.useServerMemory },\n  );\n\n  copilotKit.updateRuntime({\n    selfManagedAgents: {\n      ...copilotKit.agents(),\n      [config.agentId]: httpAgent,\n    },\n  });\n\n  for (const tool of config.frontendTools ?? []) {\n    registerFrontendTool({\n      ...tool,\n      component: tool.component || FallbackToolCard,\n      agentId: config.agentId,\n    });\n  }\n\n  [...]\n}<\/code><\/pre>\n<p>The call to <code>updateRuntime<\/code> adds a new entry to the agents registered with CopilotKit. <a href=\"https:\/\/github.com\/angular-architects\/flights42\/blob\/copilotkit\/src\/app\/domains\/shared\/util-copilotkit\/app-http-agent.ts\"><code>AppHttpAgent<\/code><\/a> is a lean subclass of the <code>HttpAgent<\/code> from the AG-UI SDK. Among other things, it implements the <code>useServerMemory<\/code> option: if the server remembers the chat history, <code>AppHttpAgent<\/code> filters already transmitted messages out of the requests.<\/p>\n<p>The subsequent loop registers the passed tools with CopilotKit using <code>registerFrontendTool<\/code>. In doing so, it binds each tool to the <code>agentId<\/code> \u2013 which keeps the tool definitions themselves agnostic and reusable. The <code>FallbackToolCard<\/code> set as the default here is discussed further below.<\/p>\n<p>A pleasant side effect: <code>registerFrontendTool<\/code> remembers the current injection context and later runs the tool handlers within it. That is why the handlers may use Angular's <code>inject<\/code>, as shown in the next section.<\/p>\n<p>The rest of the function, hinted at by <code>[...]<\/code>, takes care of displaying tool calls that the client does not execute itself \u2013 first and foremost the agent's server-side tools. We will look at it further below.<\/p>\n<h2>Defining Client-Side Tools<\/h2>\n<p>The demo project describes the individual tools with the helper function <code>createFrontendTool<\/code>:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/ticketing\/ai\/tools\/find-flights.tool.ts\nimport { inject } from &#039;@angular\/core&#039;;\nimport { Router } from &#039;@angular\/router&#039;;\nimport { z } from &#039;zod&#039;;\n\nimport { createFrontendTool } from &#039;..\/..\/..\/shared\/util-copilotkit\/tool-definition&#039;;\n\n[...]\n\nexport const findFlightsTool = createFrontendTool({\n  name: &#039;findFlights&#039;,\n  description: `Searches for flights and redirects the user to the result`,\n  parameters: z.object({\n    from: z.string().describe(&#039;airport of departure&#039;),\n    to: z.string().describe(&#039;airport of destination&#039;),\n  }),\n  handler: async ({ from, to }) =&gt; {\n    const store = inject(FlightStore);\n    const router = inject(Router);\n    store.updateFilter(from, to);\n    await router.navigate([&#039;\/ticketing\/booking\/flight-search&#039;]);\n    return { ok: true };\n  },\n});<\/code><\/pre>\n<p>Besides a name, a description, and parameter definitions based on <code>Zod<\/code>, the tool definition also includes a <code>handler<\/code>. When the agent requests the tool, CopilotKit executes this handler \u2013 including a working <code>inject<\/code> thanks to the injection context mentioned earlier.<\/p>\n<p>The parameter object conforms to the TypeScript type inferred from the Zod schema. In our case, that is an object with the properties <code>from<\/code> and <code>to<\/code>. The implementation shown triggers a flight search by passing these search criteria to the <code>FlightStore<\/code> and then navigates the user to the results page.<\/p>\n<p>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 <code>getLoadedFlightsTool<\/code>, which informs the agent about the flights currently displayed to the user by the application:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/ticketing\/ai\/tools\/get-loaded-flights.tool.ts\nexport const getLoadedFlightsTool = createFrontendTool({\n  name: &#039;getLoadedFlights&#039;,\n  description: `Returns the currently loaded\/displayed flights`,\n  parameters: z.object({}),\n  handler: async () =&gt; {\n    const store = inject(FlightStore);\n    return store.flightsValue().map(toFlightInfo);\n  },\n});<\/code><\/pre>\n<h2>The Inner Workings of <code>createFrontendTool<\/code><\/h2>\n<p>A look behind the scenes reveals that <code>createFrontendTool<\/code> is a pure identity function at runtime. It takes the tool definition and returns it unchanged \u2013 it registers nothing and injects nothing:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/shared\/util-copilotkit\/tool-definition.ts\nimport { type FrontendToolConfig } from &#039;@copilotkit\/angular&#039;;\n\nexport function createFrontendTool&lt;Args extends Record&lt;string, unknown&gt;&gt;(\n  tool: FrontendToolConfig&lt;Args&gt;,\n): FrontendToolConfig&lt;Args&gt; {\n  return tool;\n}<\/code><\/pre>\n<p>The point of this pass-through lies in type inference: TypeScript derives the generic type parameter <code>Args<\/code> from the Zod schema passed in <code>parameters<\/code> and applies it to the entire definition. As a result, the <code>handler<\/code> knows the types of its parameters \u2013 in the case of <code>findFlights<\/code>, <code>from<\/code> and <code>to<\/code> as <code>string<\/code> \u2013 without the tool definition having to annotate them explicitly.<\/p>\n<p>Without the helper function, only less convenient alternatives would remain: typing the definition by hand as <code>FrontendToolConfig&lt;...&gt;<\/code> and thus maintaining the parameter types redundantly alongside the schema, or forgoing typing altogether and only noticing typos in the handler at runtime.<\/p>\n<h2>Widgets Are Tools with a Component<\/h2>\n<p>For widgets such as the flight card, CopilotKit does not provide a dedicated concept \u2013 and does not need one. A widget is simply a frontend tool that has an Angular component assigned via the <code>component<\/code> property. When the agent calls the tool, CopilotKit renders that component in the chat history and passes it the parameters supplied by the LLM:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/ticketing\/ui\/flight-widget.ts\nconst flightSchema = z.object({\n  id: z.number().describe(&#039;The flight id&#039;),\n  from: z.string().describe(&#039;Departure city&#039;),\n  to: z.string().describe(&#039;Arrival city&#039;),\n  date: z.string().describe(&#039;Departure date in ISO format&#039;),\n  delay: z.number().describe(&#039;Delay in minutes&#039;),\n});\n\nconst flightWidgetSchema = z.object({\n  flight: flightSchema,\n  status: z.enum([&#039;booked&#039;, &#039;other&#039;]).describe(&#039;Status of the flight&#039;),\n});\n\nexport const flightWidget = createFrontendTool({\n  name: &#039;flightWidget&#039;,\n  description: `Displays a concrete flight as an interactive card.\nUse it when referring to one or more specific flights.`,\n  parameters: flightWidgetSchema,\n  component: FlightWidget,\n  followUp: false,\n  handler: async () =&gt; ({ shown: true }),\n});<\/code><\/pre>\n<p>Here, the schema does not just describe the parameters of a function call but at the same time the data the component is supplied with. The option <code>followUp: false<\/code> tells CopilotKit that no further roundtrip to the agent is needed after displaying the widget \u2013 the widget concludes the response.<\/p>\n<p>Important to note: <code>followUp: false<\/code> only controls the client. So that the model treats such calls as the end of its turn as well, <code>createFrontendTool<\/code> in the demo project automatically appends a corresponding hint to the tool description and thus to the prompt.<\/p>\n<p>The assigned component implements the <code>ToolRenderer<\/code> interface and takes the tool call as an input. Through <code>toolCall().args<\/code>, it accesses the values passed by the LLM and validated against the schema:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/ticketing\/ui\/flight-widget.ts\nimport { type AngularToolCall, type ToolRenderer } from &#039;@copilotkit\/angular&#039;;\n\n@Component({\n  selector: &#039;app-flight-widget&#039;,\n  imports: [FlightCard, RouterLink],\n  template: `\n    @let flight = toolCall().args.flight;\n    @if (flight) {\n      &lt;app-flight-card [item]=&quot;flight&quot; [readonly]=&quot;true&quot;&gt;\n        [...]\n      &lt;\/app-flight-card&gt;\n    }\n  `,\n})\nexport class FlightWidget implements ToolRenderer&lt;FlightWidgetArgs&gt; {\n  readonly toolCall = input.required&lt;AngularToolCall&lt;FlightWidgetArgs&gt;&gt;();\n  [...]\n}<\/code><\/pre>\n<h2>Sending Requests to the Agent<\/h2>\n<p>The application's chat component \u2013 <code>AssistantChat<\/code> in the demo project \u2013 obtains the agent store through the function shown earlier and derives its state from it:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/shared\/ui-assistant\/assistant-chat\/assistant-chat.ts\nimport { CopilotKit } from &#039;@copilotkit\/angular&#039;;\n\n[...]\n\nexport class AssistantChat {\n  private readonly copilotKit = inject(CopilotKit);\n\n  protected readonly store = injectTicketingAgentStore();\n\n  protected readonly messages = computed(() =&gt; this.store().messages());\n  protected readonly isRunning = computed(() =&gt; this.store().isRunning());\n\n  protected submit(): void {\n    void sendMessage(this.copilotKit, this.store, this.message());\n  }\n}<\/code><\/pre>\n<p>Sending is handled by the <code>sendMessage<\/code> function. It adds the user message to the agent and then triggers a run through CopilotKit:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/shared\/util-copilotkit\/agent-store-helper.ts\nexport async function sendMessage(\n  copilotKit: CopilotKit,\n  store: Signal&lt;AgentStore&gt;,\n  content: string,\n): Promise&lt;void&gt; {\n  const agent = store().agent;\n  agent.addMessage({ id: randomUUID(), role: &#039;user&#039;, content });\n  await copilotKit.core.runAgent({ agent });\n}<\/code><\/pre>\n<p>The detour via <code>copilotKit.core.runAgent<\/code> is important: only this way do the previously registered frontend tools take part in the run. Now the client merely needs to display the response messages returned by the agent via AG-UI in the chat history.<\/p>\n<h2>Presenting the Chat History in Angular Templates<\/h2>\n<p>The entire chat history resides in the agent store's <code>messages<\/code> signal. The template iterates over the messages and, besides the textual response (<code>content<\/code>), also renders the requested tool calls. The latter is handled by the <code>RenderToolCalls<\/code> component provided by CopilotKit:<\/p>\n<pre><code class=\"language-html\">&lt;!-- src\/app\/domains\/shared\/ui-assistant\/chat-messages\/chat-messages.html --&gt;\n@for (message of messages(); track message.id) {\n  @if (message.content) {\n    &lt;div&gt;{{ message.content }}&lt;\/div&gt;\n  }\n  @if (message.role === &#039;assistant&#039; &amp;&amp; message.toolCalls?.length) {\n    &lt;copilot-render-tool-calls\n      [message]=&quot;message&quot;\n      [messages]=&quot;messages()&quot;\n      [agentId]=&quot;&#039;ticketingAgent&#039;&quot; \/&gt;\n  }\n}<\/code><\/pre>\n<p>For each tool call, <code>RenderToolCalls<\/code> looks up the registered presentation component \u2013 for <code>flightWidget<\/code>, this makes the interactive flight card appear. We do not need to take care of executing client-side tools ourselves, however, because CopilotKit handles that task without any further effort on our part.<\/p>\n<p>The streamed AG-UI messages can be inspected through the browser's developer tools:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/ag-ui-stream.png\" alt=\"Streamed AG-UI messages in the browser&#039;s developer tools\" \/><\/p>\n<p>On the one hand, this makes the protocol tangible; on the other, it helps with troubleshooting.<\/p>\n<h2>Time for a First Test<\/h2>\n<p>With that, everything is in place to try out the solution; details on starting the client and the agent can be found in the README of the demo project. If we ask the assistant for flights from Graz to Hamburg, for example, the agent requests the frontend tool <code>findFlights<\/code>, and the application navigates to the result list. Questions about concrete flights are answered with the flight card registered as a widget, directly in the chat history:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/07\/copilotkit-frontend-tool.png\" alt=\"The sidecar chat answers a flight query with text and the interactive flight card\" \/><\/p>\n<p>Looking up booked flights as well as booking and cancelling them is something the agent already handles too \u2013 backed by its server-side tools. How the chat displays such calls is covered in the next section.<\/p>\n<h2>Displaying Server-Side Tool Calls<\/h2>\n<p>Frontend tools are executed by the client. The agent's server-side tools \u2013 such as <code>bookFlight<\/code> and <code>cancelFlight<\/code> \u2013 nevertheless also appear as tool calls in the chat history. There is nothing to execute on the client side here, but there is something to display. In the demo project, this task is handled by the <code>FallbackToolCard<\/code> \u2013 the very component that <code>initAgentStore<\/code> already used above as the default for frontend tools without their own presentation (<code>component: tool.component || FallbackToolCard<\/code>). Here it additionally takes on the role of a wildcard renderer for all remaining tool calls.<\/p>\n<p>Since the fallback has to cope with any arbitrary tool, the Zod schema for its arguments is correspondingly generic \u2013 a record with arbitrary keys and values:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/shared\/util-copilotkit\/fallback-tool-card.ts\nconst fallbackToolArgsSchema = z.record(z.string(), z.unknown());\n\nexport type FallbackToolArgs = z.infer&lt;typeof fallbackToolArgsSchema&gt;;<\/code><\/pre>\n<p>As usual, the presentation component implements the <code>ToolRenderer<\/code> interface. It displays the name of the call; on click, it reveals the passed parameters:<\/p>\n<pre><code class=\"language-ts\">@Component({\n  selector: &#039;app-fallback-tool-card&#039;,\n  template: `\n    &lt;button type=&quot;button&quot; class=&quot;tool-call&quot; (click)=&quot;toggle()&quot;&gt;\n      &lt;span class=&quot;tool-call-label&quot;&gt;Tool Call: {{ toolName() }}&lt;\/span&gt;\n      &lt;span class=&quot;tool-call-caret&quot;&gt;{{ expanded() ? &#039;\u25be&#039; : &#039;\u25b8&#039; }}&lt;\/span&gt;\n    &lt;\/button&gt;\n    @if (expanded()) {\n      &lt;pre class=&quot;tool-call-args&quot;&gt;{{ prettyArgs() }}&lt;\/pre&gt;\n    }\n  `,\n})\nexport class FallbackToolCard implements ToolRenderer&lt;FallbackToolArgs&gt; {\n  readonly toolCall = input.required&lt;AngularToolCall&lt;FallbackToolArgs&gt;&gt;();\n\n  protected readonly expanded = signal(false);\n\n  protected readonly toolName = computed(\n    () =&gt; this.toolCall().name ?? &#039;unknown&#039;,\n  );\n\n  protected readonly prettyArgs = computed(() =&gt;\n    JSON.stringify(this.toolCall().args ?? {}, null, 2),\n  );\n\n  protected toggle(): void {\n    this.expanded.update((value) =&gt; !value);\n  }\n}<\/code><\/pre>\n<p>The helper function <code>createRenderToolCall<\/code> combines schema and component into a registration object \u2013 like <code>createFrontendTool<\/code>, an identity function that merely provides type safety. A <code>handler<\/code> is deliberately missing: execution is handled by the server, and the component is pure presentation. The name <code>&#039;*&#039;<\/code> marks the renderer as a wildcard and thus as a fallback:<\/p>\n<pre><code class=\"language-ts\">export const fallbackToolCard = createRenderToolCall({\n  name: &#039;*&#039;,\n  args: fallbackToolArgsSchema,\n  component: FallbackToolCard,\n});<\/code><\/pre>\n<p>Registration is handled by the part of <code>initAgentStore<\/code> that was hidden behind <code>[...]<\/code> above:<\/p>\n<pre><code class=\"language-ts\">\/\/ src\/app\/domains\/shared\/util-copilotkit\/init-agent-store.ts\nimport { registerRenderToolCall } from &#039;@copilotkit\/angular&#039;;\n\n[...]\n\nexport function initAgentStore(config: InitAgentStoreConfig): void {\n  [...]\n\n  const registeredFallback = copilotKit\n    .toolCallRenderConfigs()\n    .find((renderer) =&gt; renderer.name === &#039;*&#039;);\n  if (!registeredFallback) {\n    registerRenderToolCall(fallbackToolCard);\n  }\n\n  for (const toolCall of config.toolCallRenderer ?? []) {\n    registerRenderToolCall({ ...toolCall, agentId: config.agentId });\n  }\n}<\/code><\/pre>\n<p>The function first checks whether a wildcard renderer is already registered and otherwise registers the <code>fallbackToolCard<\/code>. After that, it registers the presentation components passed via the <code>toolCallRenderer<\/code> property and binds them \u2013 just like the frontend tools \u2013 to the <code>agentId<\/code>.<\/p>\n<h2>Another Test: Server-Side Tools in Action<\/h2>\n<p>That makes a second look at the application worthwhile: if we ask the assistant whether we have already booked a flight to Paris, the agent reaches for <code>findBookedFlightsTool<\/code> \u2013 a server-side tool. Its execution takes place entirely on the server; the client merely visualizes the call through the registered <code>toolCallRenderer<\/code> as an expandable card including its parameters. The agent then presents the answer with the familiar flight widget:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/07\/copilotkit-server-tool-call.png\" alt=\"The sidecar chat shows the server-side tool call findBookedFlightsTool as an expandable card and the answer as a flight card\" \/><\/p>\n<p>This way, every server-side tool call remains transparently traceable for users.<\/p>\n<h2>Summary<\/h2>\n<p>With <code>@copilotkit\/angular<\/code>, AG-UI can easily be integrated into Angular. Client-side tools and widgets are described uniformly as frontend tools, with widgets merely bringing along an additional presentation component. Server-side tool calls appear in the chat history as well \u2013 rendered by a default renderer registered as a wildcard. The agent store exposes the chat history as a signal, and the included <code>RenderToolCalls<\/code> component brings the registered components to the screen within the chat.<\/p>\n<h2>The Next Step<\/h2>\n<p>With AG-UI, the communication between frontend and agent is settled \u2013 the next series shows how the LLM itself can compose the UI from layout, display, and input primitives, without shipping new frontend code for every use case.<\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/en\/blog\/a2ui-how-ai-generates-dynamic-uis-at-runtime\/\" class=\"button\" style=\"text-decoration:none !important\"><strong>Next article \u2192<\/strong><\/a><\/p>\n<hr \/>\n<p><div style=\"\nmargin: 8px 0;\npadding: 22px;\nborder: 1px solid #e5e7eb;\nborder-radius: 14px;\nbackground: #f8fafc;\n\"><\/p>\n<h3 style=\"margin-top:0\">Interested in production-ready Agentic UI architectures?<\/h3>\n<p>In my workshop, we dig into AG-UI, A2UI, MCP Apps, HITL patterns, and modern Angular architectures for real-world agentic systems.<\/p>\n<p><a href=\"https:\/\/www.angulararchitects.io\/en\/training\/agentic-ai-with-angular-architecture-patterns\/\"><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/sujet-workshop-en.png\" width=\"400\" alt=\"Workshop: Agentic AI with Angular \u2013 AG-UI, A2UI, MCP Apps & HITL patterns\" style=\"cursor:pointer !important\"><\/a><\/p>\n<p><a style=\"cursor:pointer !important\" href=\"https:\/\/www.angulararchitects.io\/en\/training\/agentic-ai-with-angular-architecture-patterns\/\">See all details \u2192<\/a>\n<\/div>\n<\/p>\n<h2>FAQ<\/h2>\n<h3>How Can AG-UI Be Integrated into Angular?<\/h3>\n<p>With <code>@copilotkit\/angular<\/code>, an Angular integration is available that builds directly on the AG-UI SDK. It manages the agent, executes client-side tools, and exposes the chat history as a signal-based agent store.<\/p>\n<h3>What Is an Agent Store?<\/h3>\n<p>The agent store represents a concrete agent together with its state. <code>injectAgentStore<\/code> returns it as a <code>Signal&lt;AgentStore&gt;<\/code> that offers, among other things, the chat history (<code>messages<\/code>) and the execution state (<code>isRunning<\/code>) as signals.<\/p>\n<h3>What Is the Helper Function <code>initAgentStore<\/code> For?<\/h3>\n<p>CopilotKit currently offers no way to initialize agent stores for lazy-loaded feature areas after the fact. <code>initAgentStore<\/code> closes this gap: it registers the agent at runtime as a self-managed agent and registers the frontend tools for its <code>agentId<\/code>.<\/p>\n<h3>How Do Widgets End Up in the Chat History?<\/h3>\n<p>Widgets are frontend tools with an assigned Angular component. When the agent requests such a tool, the CopilotKit component <code>RenderToolCalls<\/code> renders the registered component in the chat history and supplies it with the parameters passed by the LLM and validated against the Zod schema.<\/p>\n<h3>How Can Server-Side Tool Calls Be Visualized?<\/h3>\n<p>Server-side tool calls also appear in the chat history, even though the client does not execute them. A wildcard renderer described with <code>createRenderToolCall<\/code> (<code>name: &#039;*&#039;<\/code>) displays them by default as an expandable card including its parameters; further presentation components can be passed through the <code>toolCallRenderer<\/code> property of <code>initAgentStore<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is post 4 of 9 in the series &ldquo;Agentic Angular&rdquo; Understanding AG-UI: The Standard for Agentic User Interfaces AG-UI in Practice: The SDK for TypeScript AG-UI End to End: Connecting Server and Client Agentic UI with Angular, CopilotKit, and AG-UI A2UI: How AI Generates Dynamic UIs at Runtime Integrating A2UI with AG-UI and CopilotKit [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":33201,"comment_status":"open","ping_status":"open","sticky":true,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-33203","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","post_series-agentic-angular"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Agentic UI with Angular, CopilotKit, and AG-UI - ANGULARarchitects<\/title>\n<meta name=\"description\" content=\"Implementing AG-UI with Angular and CopilotKit&quot;description: &quot;How can AG-UI be integrated into Angular applications? This article shows the Angular integration of CopilotKit: agent stores, client-side tools, widgets in the chat history, and a small helper function for lazy-loaded agents.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Agentic UI with Angular, CopilotKit, and AG-UI - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"Implementing AG-UI with Angular and CopilotKit&quot;description: &quot;How can AG-UI be integrated into Angular applications? This article shows the Angular integration of CopilotKit: agent stores, client-side tools, widgets in the chat history, and a small helper function for lazy-loaded agents.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-20T12:57:18+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-07-24T07:52:27+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/Kopie-von-Kopie-von-Kopie-von-Kopie-von-Kopie-von-Blue-White-and-Red-Modern-Digital-Marketing-Agency-Facebook-Ad-1200-x-628-px-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"628\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Manfred Steyer\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/Kopie-von-Kopie-von-Kopie-von-Kopie-von-Kopie-von-Blue-White-and-Red-Modern-Digital-Marketing-Agency-Facebook-Ad-1200-x-628-px-1.png\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Manfred Steyer\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\"},\"author\":{\"name\":\"Manfred Steyer\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\"},\"headline\":\"Agentic UI with Angular, CopilotKit, and AG-UI\",\"datePublished\":\"2026-04-20T12:57:18+00:00\",\"dateModified\":\"2026-07-24T07:52:27+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\"},\"wordCount\":2312,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\",\"name\":\"Agentic UI with Angular, CopilotKit, and AG-UI - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg\",\"datePublished\":\"2026-04-20T12:57:18+00:00\",\"dateModified\":\"2026-07-24T07:52:27+00:00\",\"description\":\"Implementing AG-UI with Angular and CopilotKit\\\"description: \\\"How can AG-UI be integrated into Angular applications? This article shows the Angular integration of CopilotKit: agent stores, client-side tools, widgets in the chat history, and a small helper function for lazy-loaded agents.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg\",\"width\":1000,\"height\":666},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Agentic UI with Angular, CopilotKit, and AG-UI\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\",\"name\":\"Manfred Steyer\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g\",\"caption\":\"Manfred Steyer\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Agentic UI with Angular, CopilotKit, and AG-UI - ANGULARarchitects","description":"Implementing AG-UI with Angular and CopilotKit\"description: \"How can AG-UI be integrated into Angular applications? This article shows the Angular integration of CopilotKit: agent stores, client-side tools, widgets in the chat history, and a small helper function for lazy-loaded agents.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/","og_locale":"en_US","og_type":"article","og_title":"Agentic UI with Angular, CopilotKit, and AG-UI - ANGULARarchitects","og_description":"Implementing AG-UI with Angular and CopilotKit\"description: \"How can AG-UI be integrated into Angular applications? This article shows the Angular integration of CopilotKit: agent stores, client-side tools, widgets in the chat history, and a small helper function for lazy-loaded agents.","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/","og_site_name":"ANGULARarchitects","article_published_time":"2026-04-20T12:57:18+00:00","article_modified_time":"2026-07-24T07:52:27+00:00","og_image":[{"width":1200,"height":628,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/Kopie-von-Kopie-von-Kopie-von-Kopie-von-Kopie-von-Blue-White-and-Red-Modern-Digital-Marketing-Agency-Facebook-Ad-1200-x-628-px-1.png","type":"image\/png"}],"author":"Manfred Steyer","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/Kopie-von-Kopie-von-Kopie-von-Kopie-von-Kopie-von-Blue-White-and-Red-Modern-Digital-Marketing-Agency-Facebook-Ad-1200-x-628-px-1.png","twitter_misc":{"Written by":"Manfred Steyer","Est. reading time":"16 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/"},"author":{"name":"Manfred Steyer","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a"},"headline":"Agentic UI with Angular, CopilotKit, and AG-UI","datePublished":"2026-04-20T12:57:18+00:00","dateModified":"2026-07-24T07:52:27+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/"},"wordCount":2312,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/","name":"Agentic UI with Angular, CopilotKit, and AG-UI - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg","datePublished":"2026-04-20T12:57:18+00:00","dateModified":"2026-07-24T07:52:27+00:00","description":"Implementing AG-UI with Angular and CopilotKit\"description: \"How can AG-UI be integrated into Angular applications? This article shows the Angular integration of CopilotKit: agent stores, client-side tools, widgets in the chat history, and a small helper function for lazy-loaded agents.","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/04\/shutterstock_2567832395.jpg","width":1000,"height":666},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/implementing-ag-ui-with-angular\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Agentic UI with Angular, CopilotKit, and AG-UI"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a","name":"Manfred Steyer","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/8778dfb353992fa3a0d909beee085a088891e5bfce65cdb3631801da527cf11b?s=96&d=mm&r=g","caption":"Manfred Steyer"}}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33203","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=33203"}],"version-history":[{"count":10,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33203\/revisions"}],"predecessor-version":[{"id":34477,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33203\/revisions\/34477"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/33201"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=33203"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=33203"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=33203"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}