{"id":33332,"date":"2026-05-01T21:34:53","date_gmt":"2026-05-01T19:34:53","guid":{"rendered":"https:\/\/www.angulararchitects.io\/?p=33332"},"modified":"2026-05-01T22:43:14","modified_gmt":"2026-05-01T20:43:14","slug":"custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/","title":{"rendered":"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs"},"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-angular69f90f6daf9fa\" 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-angular69f90f6daf9fa\"\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 6 of 6 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\/implementing-ag-ui-with-angular\/\">Implementing AG-UI with Angular<\/a><\/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 in Angular<\/a><\/li>\n\t\t\t\t\t\t\t\t\t<li><span class=\"wp-post-series-box__current\">Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs<\/span><\/li>\n\t\t\t\t\t\t\t<\/ol>\n\t\t<\/div>\n\t<\/div>\n<p><em>Domain-specific widgets as first-class building blocks for the language model.<\/em><\/p>\n<p>As soon as domain-specific concepts come into play, A2UI's Basic Catalog rarely suffices: a flight booking, a boarding pass, or a bonus program quickly look arbitrary as generic cards and lists. With <em>Custom Catalogs<\/em>, A2UI therefore provides a clear mechanism to make your own domain-appropriate components and functions available to the language model \u2013 without sacrificing the lean, declarative character of the protocol.<\/p>\n<p>This third and final part of the series shows how to define such Custom Catalogs in Angular, register them with the renderer, and integrate them with the AG-UI abstraction introduced in the second part.<\/p>\n<p>\ud83d\udcc2 <a href=\"https:\/\/github.com\/angular-architects\/flights42\/tree\/a2ui-dynamic\">Source Code<\/a> (see branch <code>a2ui-dynamic<\/code>)<\/p>\n<h2>What Are Custom Catalogs in A2UI?<\/h2>\n<p>A <em>Custom Catalog<\/em> extends A2UI with your own, domain-driven components and functions that the language model may reference like any other component. Custom Catalogs often represent a superset of the Basic Catalog, so that in addition to your own building blocks they also include the well-known general-purpose UI building blocks. From the renderer's perspective, processing remains the same; the LLM merely receives a larger toolbox.<\/p>\n<p>In what follows, we extend the passenger card introduced in the first part with our own <code>MilesProgress<\/code> component, which visualizes the progress to the next bonus tier:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/miles-progress.png\" alt=\"Custom Catalog in A2UI: MilesProgress widget with a progress bar to the next bonus tier in an Angular demo\" style=\"max-width: 400px\" \/><\/p>\n<h2>Creating Your Own Components for the Custom Catalog<\/h2>\n<p>At its core, an A2UI component in Angular is just a regular Angular component. However, it receives the inputs that the agent transmits via A2UI through a defined <em>Context<\/em> object. The following listing shows the definition of such a context for our <code>MilesProgress<\/code> component. The <code>passenger<\/code> property is declared as a <code>BoundProperty<\/code>: it can hold either concrete data or represent a binding to the data model:<\/p>\n<pre><code class=\"language-typescript\">export interface MilesProgressContext {\n  passenger: BoundProperty&lt;Passenger&gt;;\n}<\/code><\/pre>\n<p>The corresponding component receives this context via the <code>props<\/code> <code>InputSignal<\/code>:<\/p>\n<pre><code class=\"language-typescript\">@Component({\n  selector: &#039;app-miles-progress&#039;,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n  imports: [DecimalPipe],\n  template: `\n    &lt;section class=&quot;miles-progress&quot;&gt;\n      &lt;h3&gt;Miles Progress&lt;\/h3&gt;\n      &lt;p class=&quot;current&quot;&gt;{{ passenger().bonusMiles | number }}&lt;\/p&gt;\n      &lt;p class=&quot;remaining&quot;&gt;\n        {{ remainingMiles() | number }} miles to {{ nextThreshold() | number }}\n      &lt;\/p&gt;\n      &lt;div aria-hidden=&quot;true&quot; class=&quot;track&quot;&gt;\n        &lt;div class=&quot;fill&quot; [style.width.%]=&quot;progressPercent()&quot;&gt;&lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  `,\n  styleUrl: &#039;.\/miles-progress.css&#039;,\n})\nexport class MilesProgress {\n  readonly props = input&lt;MilesProgressContext&gt;(initialContext);\n  readonly surfaceId = input.required&lt;string&gt;();\n  readonly componentId = input.required&lt;string&gt;();\n  readonly dataContextPath = input(&#039;\/&#039;);\n\n  protected readonly passenger = computed(() =&gt; this.props().passenger.value());\n\n  protected readonly nextThreshold = computed(() =&gt;\n    calcNextThreshold(this.passenger().bonusMiles),\n  );\n\n  protected readonly remainingMiles = computed(() =&gt;\n    calcRemainingMiles(this.nextThreshold(), this.passenger().bonusMiles),\n  );\n\n  protected readonly progressPercent = computed(() =&gt;\n    calcProgressPercent(this.nextThreshold(), this.passenger().bonusMiles),\n  );\n}<\/code><\/pre>\n<p>The <code>MilesProgress<\/code> component shown reads the current bonus miles from the context and uses a computed signal to calculate the number of miles still needed to reach the next bonus tier. It also displays a progress indicator for that goal.<\/p>\n<p>So that the renderer knows the <code>MilesProgress<\/code> component and can instantiate it correctly, a description of its inputs in the form of a schema is also required. The renderer from the A2UI team uses the popular library <a href=\"https:\/\/zod.dev\/\">Zod<\/a> for this:<\/p>\n<pre><code class=\"language-typescript\">import {\n  AngularComponentImplementation,\n} from &#039;@a2ui\/angular\/v0_9&#039;;\nimport { z } from &#039;zod\/v3&#039;;\n\n[...]\n\nconst passengerSchema = z\n  .object({\n    id: z.number(),\n    firstName: z.string(),\n    lastName: z.string(),\n    bonusMiles: z.number(),\n  })\n  .strict();\n\nconst pathBindingSchema = z.object({ path: z.string() }).strict();\n\nconst passengerOrPathSchema = z.union([passengerSchema, pathBindingSchema]);\n\nconst milesProgressSchema = z\n  .object({\n    passenger: passengerOrPathSchema.optional(),\n  })\n  .strict();\n\nconst milesProgressEntry = {\n  name: &#039;MilesProgress&#039;,\n  component: MilesProgress,\n  schema: milesProgressSchema,\n} as unknown as AngularComponentImplementation;\n\/\/ ^^^ unknown prevents typing issue in current version<\/code><\/pre>\n<p>The schema specifies that the <code>passenger<\/code> property can be passed either as an object (<code>passengerSchema<\/code>) or as a data binding (<code>pathBindingSchema<\/code>) with a <code>path<\/code> property. The constant <code>milesProgressEntry<\/code> bundles the component's name and implementation as well as the schema into a single unit that the catalog can later include.<\/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\">New: Agentic UI with Angular<\/h3>\n<p>If you don\u2019t just want to integrate A2UI but embed it into a scalable architecture:<br \/>\nIn my book Agentic UI with Angular, I cover the underlying 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>Your Own Functions for the Custom Catalog<\/h2>\n<p>In addition to components, a Custom Catalog can also provide its own functions. These complement the standard functions included in the Basic Catalog, such as <code>formatNumber<\/code> and <code>formatDate<\/code>. The following listing shows a small helper function <code>formatId<\/code>, which converts a numeric ID into a readable string like <code>P-0042<\/code>. To describe the expected arguments, Zod is used once again:<\/p>\n<pre><code class=\"language-typescript\">import type { FunctionImplementation } from &#039;@a2ui\/web_core\/v0_9&#039;;\nimport { z, type ZodTypeAny } from &#039;zod\/v3&#039;;\n\nconst formatIdSchema = z\n  .object({\n    value: z.number(),\n  })\n  .strict();\n\nexport const formatIdImplementation = {\n  name: &#039;formatId&#039;,\n  returnType: &#039;string&#039;,\n  schema: formatIdSchema as unknown as ZodTypeAny,\n  execute: (args: Record&lt;string, unknown&gt;) =&gt; {\n    const { value } = formatIdSchema.parse(args);\n    const normalizedValue = Math.max(0, Math.trunc(value));\n    return `P-${String(normalizedValue).padStart(4, &#039;0&#039;)}`;\n  },\n} as unknown as FunctionImplementation;\n\/\/ ^^^ unknown prevents typing issue in current version<\/code><\/pre>\n<p>With components and functions, the two central building blocks of a Custom Catalog are defined. As the next step, we need to make the catalog available to the renderer.<\/p>\n<h2>Registering a Custom Catalog with the A2UI Renderer<\/h2>\n<p>A Custom Catalog inherits from <code>BasicCatalogBase<\/code> and passes a unique ID, the list of additional components, and a list of functions in the constructor:<\/p>\n<pre><code class=\"language-typescript\">@Injectable({ providedIn: &#039;root&#039; })\nexport class CustomCatalog extends BasicCatalogBase {\n  constructor() {\n    super({\n      id: &#039;https:\/\/example.com\/catalogs\/flights42-a2ui-demo&#039;,\n      extraComponents: [milesProgress],\n      functions: [...BASIC_FUNCTIONS, formatIdImplementation],\n    });\n  }\n}<\/code><\/pre>\n<p>Analogous to the <code>BasicCatalog<\/code> of the Angular adapter, we also provide our <code>CustomCatalog<\/code> as an Angular service with <code>providedIn: &#039;root&#039;<\/code>. So that the renderer uses the new catalog, it just needs to be referenced in the configuration:<\/p>\n<pre><code class=\"language-typescript\">import {\n  A2UI_RENDERER_CONFIG,\n  A2uiRendererService,\n  provideMarkdownRenderer,\n} from &#039;@a2ui\/angular\/v0_9&#039;;\nimport {\n  ApplicationConfig,\n  inject,\n  provideBrowserGlobalErrorListeners,\n} from &#039;@angular\/core&#039;;\nimport { marked } from &#039;marked&#039;;\nimport { CustomCatalog } from &#039;.\/custom-catalog\/custom-catalog&#039;;\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    provideBrowserGlobalErrorListeners(),\n    {\n      provide: A2UI_RENDERER_CONFIG,\n      useFactory: () =&gt; ({\n        catalogs: [inject(CustomCatalog)],\n      }),\n    },\n    provideMarkdownRenderer(async (markdown) =&gt;\n      marked.parse(String(markdown ?? &#039;&#039;)),\n    ),\n    A2uiRendererService,\n  ],\n};<\/code><\/pre>\n<p>Once the catalog is registered, the example application can use the <code>MilesProgress<\/code> component like any other component in an A2UI message. The following listing shows an excerpt of an <code>updateComponents<\/code> message in which the <code>MilesProgress<\/code> component is displayed alongside the existing passenger card:<\/p>\n<pre><code class=\"language-typescript\">updateComponents: {\n  surfaceId,\n  components: [\n    {\n      id: &#039;root&#039;,\n      component: &#039;Column&#039;,\n      children: [&#039;passenger-card&#039;, &#039;miles-progress&#039;],\n    },\n    [...]\n    {\n      id: &#039;miles-progress&#039;,\n      component: &#039;MilesProgress&#039;,\n      passenger: { path: &#039;\/passenger&#039; },\n    },\n  ],\n}<\/code><\/pre>\n<h2>Wiring Custom Components into the AG-UI Abstraction<\/h2>\n<p>So far we've registered Custom Components for the standalone A2UI renderer. In combination with the AG-UI abstraction introduced in the second part of this series, the approach looks similar but somewhat more convenient: the abstraction provides its own helper functions that encapsulate both the component description and its registration.<\/p>\n<p>So that the A2UI renderer in the sidecar can show not only the components of the Basic Catalog but also your own widgets, Custom Components can be added. The example project used here contains a <code>TicketWidget<\/code> that represents a boarding pass:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/custom-a2ui-component.png\" alt=\"Custom Catalog in A2UI: TicketWidget as a boarding pass with flight number, route, and date, embedded in a sidecar chat\" style=\"max-width: 400px\" \/><\/p>\n<p>The helper function used here, <code>createCustomComponent<\/code>, takes the name, the description, the component implementation, and a Zod schema that describes the component's properties:<\/p>\n<pre><code class=\"language-typescript\">import {\n  A2uiCustomCatalogComponent,\n  binding,\n  createCustomComponent,\n} from &#039;@internal\/ag-ui-client&#039;;\nimport { z } from &#039;zod&#039;;\nimport { TicketWidget } from &#039;.\/ticket\/ticket-widget&#039;;\n\nexport const ticketWidgetEntry = createCustomComponent({\n  name: &#039;TicketWidget&#039;,\n  description: &#039;A boarding-pass-style widget ...&#039;,\n  component: TicketWidget,\n  schema: z\n    .object({\n      ticketId: binding(z.union([z.string(), z.number()])),\n      from: binding(z.string()),\n      to: binding(z.string()),\n      date: binding(z.string()),\n      delay: binding(z.number()).optional(),\n    })\n    .strict(),\n});\n\nexport const ticketingExtraComponents: A2uiCustomCatalogComponent[] = [\n  ticketWidgetEntry,\n];<\/code><\/pre>\n<p>The <code>binding<\/code> schema represents those fields that the LLM may either set directly or wire to values from the data model via a <code>path<\/code> binding.<\/p>\n<p>So that a Custom Catalog that extends the Basic Catalog with this component is used, we put the description of our <code>TicketWidget<\/code> into an array and pass it to <code>provideA2uiCatalog<\/code>:<\/p>\n<pre><code class=\"language-typescript\">import { provideMarkdownRenderer } from &#039;@a2ui\/angular\/v0_9&#039;;\nimport { provideA2uiCatalog } from &#039;@internal\/ag-ui-client&#039;;\nimport { marked } from &#039;marked&#039;;\nimport { ticketingExtraComponents } from &#039;.\/domains\/ticketing\/ai\/custom-catalog\/ticketing-extra-components&#039;;\n\n[...]\n\nexport const appConfig: ApplicationConfig = {\n  providers: [\n    [...]\n    provideA2uiCatalog({\n      id: &#039;https:\/\/angulararchitects.io\/custom_catalog.json&#039;,\n      components: ticketingExtraComponents,\n      sendCatalogDescription: true,\n    }),\n    provideMarkdownRenderer(async (markdown) =&gt;\n      marked.parse(String(markdown ?? &#039;&#039;)),\n    ),\n  ],\n};<\/code><\/pre>\n<p>For Custom Functions, <code>provideA2uiCatalog<\/code> provides a <code>functions<\/code> property that is not used here. To ensure that the given id is unique, a URL from your own domain is typically used.<\/p>\n<h2>Security Aspect: <code>sendCatalogDescription<\/code> and Prompt Injection<\/h2>\n<p>The <code>sendCatalogDescription<\/code> flag ensures that the textual descriptions of the components are transmitted to the agent so that it can include the component catalog in the prompt for the LLM. The abstraction in the <code>libs<\/code> folder introduced in the second part derives simple examples from the schema and appends them, together with the descriptions, to the system prompt.<\/p>\n<p>This approach is very convenient for development, but in production it can be abused for prompt injection. In such an attack, an attacker injects malicious instructions into the system prompt and thereby tricks the language model into performing unintended actions.<\/p>\n<p>Therefore, in production it is advisable to set the <code>sendCatalogDescription<\/code> flag to <code>false<\/code>. As a result, the client only transmits the catalog id. The agent validates this against a list of approved catalogs and obtains the corresponding schema from a trusted registry \u2013 for example, via an internal API or database.<\/p>\n<h2>Summary<\/h2>\n<p>Custom Catalogs extend A2UI in a targeted way with your own components and functions that the language model may use like any other building block. This makes it possible to translate generic responses into domain-appropriate interfaces without losing the character of a lean, declarative protocol. Schema validation via Zod ensures clean contracts between agent and client, while the split into components and functions keeps the catalog flexible.<\/p>\n<p>In combination with the AG-UI abstraction, Custom Components can be described compactly with <code>createCustomComponent<\/code> and registered through a single provider call. Anyone planning the move to production should also consider the security aspect around <code>sendCatalogDescription<\/code> and source catalog schemas from a trusted source.<\/p>\n<p>Anyone who combines protocol, renderer, AG-UI integration, and Custom Catalogs has a solid foundation to have language models deliver not just text but real UI responses \u2013 and in a way that fits their own application.<\/p>\n<hr \/>\n<h2>FAQ<\/h2>\n<h3>What is a Custom Catalog in A2UI?<\/h3>\n<p>A Custom Catalog extends A2UI with your own, domain-driven components and functions. It often includes the Basic Catalog as a superset and additionally provides domain-specific building blocks to the language model, which the renderer processes like any other A2UI component.<\/p>\n<h3>How do you describe your own A2UI component?<\/h3>\n<p>The implementation is a regular Angular component that receives its inputs via a <code>Context<\/code> object. In addition to the component itself, a Zod schema is defined that describes the expected properties. Component, name, and schema are added together as an entry in the Custom Catalog.<\/p>\n<h3>How do you register a Custom Catalog in Angular?<\/h3>\n<p>The Custom Catalog is provided as a service with <code>providedIn: &#039;root&#039;<\/code> and referenced in the <code>A2UI_RENDERER_CONFIG<\/code> token. In the AG-UI abstraction, this is handled by the <code>provideA2uiCatalog<\/code> function, to which components and optional functions can be passed directly.<\/p>\n<h3>What is <code>sendCatalogDescription<\/code> for and when should you disable the flag?<\/h3>\n<p><code>sendCatalogDescription<\/code> transmits the textual component descriptions to the agent, which embeds them into the system prompt. This is very convenient, but can lead to prompt injection. In production, it is therefore advisable to set the flag to <code>false<\/code> and load schemas server-side from a trusted registry.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is post 6 of 6 in the series &ldquo;Agentic Angular&rdquo; Understanding AG-UI: The Standard for Agentic User Interfaces AG-UI in Practice: The SDK for TypeScript Implementing AG-UI with Angular A2UI: How AI Generates Dynamic UIs at Runtime Integrating A2UI with AG-UI in Angular Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs Domain-specific [&hellip;]<\/p>\n","protected":false},"author":25,"featured_media":33334,"comment_status":"open","ping_status":"open","sticky":false,"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-33332","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>Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs - ANGULARarchitects<\/title>\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\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"This is post 6 of 6 in the series &ldquo;Agentic Angular&rdquo; Understanding AG-UI: The Standard for Agentic User Interfaces AG-UI in Practice: The SDK for TypeScript Implementing AG-UI with Angular A2UI: How AI Generates Dynamic UIs at Runtime Integrating A2UI with AG-UI in Angular Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs Domain-specific [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2026-05-01T19:34:53+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-05-01T20:43:14+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/series-1024x536.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\/05\/series-1024x536.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=\"10 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\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\"},\"author\":{\"name\":\"Manfred Steyer\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a\"},\"headline\":\"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs\",\"datePublished\":\"2026-05-01T19:34:53+00:00\",\"dateModified\":\"2026-05-01T20:43:14+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\"},\"wordCount\":1364,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\",\"name\":\"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg\",\"datePublished\":\"2026-05-01T19:34:53+00:00\",\"dateModified\":\"2026-05-01T20:43:14+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg\",\"width\":1000,\"height\":667},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs\"}]},{\"@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":"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs - ANGULARarchitects","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\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/","og_locale":"en_US","og_type":"article","og_title":"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs - ANGULARarchitects","og_description":"This is post 6 of 6 in the series &ldquo;Agentic Angular&rdquo; Understanding AG-UI: The Standard for Agentic User Interfaces AG-UI in Practice: The SDK for TypeScript Implementing AG-UI with Angular A2UI: How AI Generates Dynamic UIs at Runtime Integrating A2UI with AG-UI in Angular Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs Domain-specific [&hellip;]","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/","og_site_name":"ANGULARarchitects","article_published_time":"2026-05-01T19:34:53+00:00","article_modified_time":"2026-05-01T20:43:14+00:00","og_image":[{"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/series-1024x536.png","type":"","width":"","height":""}],"author":"Manfred Steyer","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/series-1024x536.png","twitter_misc":{"Written by":"Manfred Steyer","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/"},"author":{"name":"Manfred Steyer","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/f3de69c1e2bdb5ba04d8d2f5f998b81a"},"headline":"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs","datePublished":"2026-05-01T19:34:53+00:00","dateModified":"2026-05-01T20:43:14+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/"},"wordCount":1364,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/","name":"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg","datePublished":"2026-05-01T19:34:53+00:00","dateModified":"2026-05-01T20:43:14+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2026\/05\/shutterstock_2383749427.jpg","width":1000,"height":667},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/custom-catalogs-in-a2ui-your-own-components-for-ai-generated-uis\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Custom Catalogs in A2UI: Your Own Components for AI-Generated UIs"}]},{"@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\/33332","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=33332"}],"version-history":[{"count":2,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33332\/revisions"}],"predecessor-version":[{"id":33384,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/33332\/revisions\/33384"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/33334"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=33332"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=33332"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=33332"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}