Chat · Guides

#title: Custom Catalogs description: Compose custom component catalogs for generative UI using ViewRegistry composition.

Custom Catalogs

Both json-render specs and A2UI surfaces render through the same ViewRegistry. Let's compose catalogs using views, withViews, and withoutViews from @threadplane/render.

#Using the Built-In A2UI Catalog

import { a2uiBasicCatalog } from '@threadplane/chat';
 
<chat [agent]="agentRef" [views]="a2uiBasicCatalog()" />

#Adding Custom Components

import { withViews } from '@threadplane/render';
import { a2uiBasicCatalog } from '@threadplane/chat';
 
const catalog = withViews(a2uiBasicCatalog(), {
  Chart: MyChartComponent,
  DataGrid: MyDataGridComponent,
});
 
<chat [agent]="agentRef" [views]="catalog" />

#Overriding Built-In Components

import { views } from '@threadplane/render';
import { a2uiBasicCatalog } from '@threadplane/chat';
 
const catalog = views({
  ...a2uiBasicCatalog(),
  Button: MyBrandedButtonComponent,
});

#Removing Components

import { withoutViews } from '@threadplane/render';
import { a2uiBasicCatalog } from '@threadplane/chat';
 
const catalog = withoutViews(a2uiBasicCatalog(), 'Modal', 'Video');

#Custom-Only (No A2UI)

import { views } from '@threadplane/render';
 
const catalog = views({
  Chart: MyChartComponent,
  Table: MyTableComponent,
});
 
<chat [agent]="agentRef" [views]="catalog" />

#No Generative UI

If you don't provide views, no generative UI renders. Specs and A2UI surfaces are silently skipped.

<chat [agent]="agentRef" />

#Writing a Custom Component

Custom components receive the standard AngularComponentInputs:

import { Component, input } from '@angular/core';
import type { AngularComponentInputs } from '@threadplane/render';
 
@Component({
  selector: 'my-chart',
  standalone: true,
  template: `<canvas #chart></canvas>`,
})
export class MyChartComponent {
  readonly data = input<number[]>([]);
  readonly title = input<string>('');
  readonly childKeys = input<string[]>([]);
  readonly spec = input.required<AngularComponentInputs['spec']>();
  readonly emit = input<(event: string) => void>(() => {});
  readonly loading = input<boolean>();
}

The component receives resolved props as inputs. childKeys and spec enable recursive child rendering, and emit triggers event handlers defined in the spec's on bindings. Spec isn't exported on its own — type the input via AngularComponentInputs['spec'] (or implement AngularComponentInputs directly).

#What's Next