Documentation
Customization
Customize Angular AI Kit components to match your design system.
Customization Options
Multiple ways to customize components
Angular AI Kit provides several customization approaches:
1. Custom Classes
Use the customClasses input to add Tailwind classes
2. CSS Variables
Override CSS custom properties for global theming
3. Wrapper Components
Create wrapper components with custom behavior
4. DI Tokens
Provide configuration via dependency injection
Using Custom Classes
The simplest way to customize appearance
Most components accept a customClasses input for additional Tailwind classes:
1<!-- Using customClasses input -->2<ai-user-message3 [content]="message.content"4 customClasses="rounded-xl shadow-lg"5/>6 7<ai-response8 [content]="response.content"9 customClasses="border-l-4 border-primary pl-4"10/>11 12<ai-chat-input13 placeholder="Ask anything..."14 customClasses="bg-muted/50"15/>Tips
- Custom classes are merged with default classes
- Use Tailwind's
!prefix for specificity if needed - Combine with CSS variables for full control
Creating Wrapper Components
Extend functionality with custom wrappers
Wrap existing components to add custom headers, footers, or behavior:
1// custom-message.component.ts2import { Component, input } from '@angular/core';3import { AiResponseComponent } from '@angular-ai-kit/core';4 5@Component({6 selector: 'app-custom-message',7 imports: [AiResponseComponent],8 template: `9 <div class="my-custom-wrapper">10 <!-- Custom header -->11 <div class="flex items-center gap-2 mb-2">12 <img [src]="avatarUrl()" class="w-8 h-8 rounded-full" />13 <span class="font-semibold">{{ name() }}</span>14 </div>15 16 <!-- Use original component -->17 <ai-response18 [content]="content()"19 [customClasses]="responseClasses"20 />21 22 <!-- Custom footer -->23 <div class="mt-2 text-xs text-muted-foreground">24 {{ timestamp() | date:'short' }}25 </div>26 </div>27 `,28 styles: `29 .my-custom-wrapper {30 padding: 1rem;31 border-radius: 0.75rem;32 background: var(--card);33 }34 `,35})36export class CustomMessageComponent {37 content = input.required<string>();38 name = input('Assistant');39 avatarUrl = input('/avatar.png');40 timestamp = input(new Date());41 42 readonly responseClasses = 'prose-sm';43}Custom Action Buttons
Add your own actions to messages
Create custom action components and inject them via slots:
1// custom-actions.component.ts2import { Component, output } from '@angular/core';3import { NgIcon, provideIcons } from '@ng-icons/core';4import {5 lucideShare,6 lucideBookmark,7 lucideFlag,8} from '@ng-icons/lucide';9import { HlmIconImports } from '@angular-ai-kit/spartan-ui/icon';10 11@Component({12 selector: 'app-custom-actions',13 imports: [NgIcon, HlmIconImports],14 viewProviders: [provideIcons({ lucideShare, lucideBookmark, lucideFlag })],15 template: `16 <div class="flex items-center gap-1">17 <button18 class="p-2 rounded hover:bg-accent"19 title="Share"20 (click)="share.emit()"21 >22 <ng-icon hlm name="lucideShare" size="sm" />23 </button>24 25 <button26 class="p-2 rounded hover:bg-accent"27 title="Bookmark"28 (click)="bookmark.emit()"29 >30 <ng-icon hlm name="lucideBookmark" size="sm" />31 </button>32 33 <button34 class="p-2 rounded hover:bg-accent text-destructive"35 title="Report"36 (click)="report.emit()"37 >38 <ng-icon hlm name="lucideFlag" size="sm" />39 </button>40 </div>41 `,42})43export class CustomActionsComponent {44 share = output<void>();45 bookmark = output<void>();46 report = output<void>();47}48 49// Usage in parent50@Component({51 template: `52 <ai-response [content]="content">53 <app-custom-actions54 slot="actions"55 (share)="handleShare()"56 (bookmark)="handleBookmark()"57 (report)="handleReport()"58 />59 </ai-response>60 `,61})62export class ParentComponent {}Custom Markdown Rendering
Override default markdown styles
Customize how markdown content is rendered:
1// custom-markdown.component.ts2import { Component, input } from '@angular/core';3import { MarkdownRendererComponent } from '@angular-ai-kit/core';4 5@Component({6 selector: 'app-custom-markdown',7 imports: [MarkdownRendererComponent],8 template: `9 <div class="custom-markdown-wrapper" [class]="wrapperClass()">10 <ai-markdown-renderer11 [content]="content()"12 [enableCodeHighlight]="true"13 />14 </div>15 `,16 styles: `17 .custom-markdown-wrapper {18 /* Override heading styles */19 :global(h1, h2, h3) {20 color: var(--primary);21 border-bottom: 2px solid var(--border);22 padding-bottom: 0.5rem;23 }24 25 /* Custom code block styling */26 :global(pre) {27 border-radius: 0.75rem;28 border: 1px solid var(--border);29 }30 31 /* Custom link styling */32 :global(a) {33 color: var(--primary);34 text-decoration: underline;35 text-underline-offset: 2px;36 }37 38 /* Custom list styling */39 :global(ul) {40 list-style-type: square;41 }42 }43 `,44})45export class CustomMarkdownComponent {46 content = input.required<string>();47 wrapperClass = input('');48}Custom Code Block
Add features like run button and copy feedback
Create an enhanced code block with additional features:
1// custom-code-block.component.ts2import { Component, input, output, computed } from '@angular/core';3import { CodeBlockComponent } from '@angular-ai-kit/core';4import { NgIcon, provideIcons } from '@ng-icons/core';5import { lucidePlay, lucideCopy, lucideCheck } from '@ng-icons/lucide';6 7@Component({8 selector: 'app-custom-code-block',9 imports: [CodeBlockComponent, NgIcon],10 viewProviders: [provideIcons({ lucidePlay, lucideCopy, lucideCheck })],11 template: `12 <div class="custom-code-block">13 <!-- Custom header -->14 <div class="flex items-center justify-between px-4 py-2 bg-muted rounded-t-lg">15 <span class="text-sm font-mono text-muted-foreground">16 {{ language() }}17 </span>18 <div class="flex gap-2">19 @if (isRunnable()) {20 <button21 class="flex items-center gap-1 text-sm text-primary hover:underline"22 (click)="run.emit(code())"23 >24 <ng-icon name="lucidePlay" size="sm" />25 Run26 </button>27 }28 <button29 class="flex items-center gap-1 text-sm hover:underline"30 (click)="copyCode()"31 >32 <ng-icon [name]="copied() ? 'lucideCheck' : 'lucideCopy'" size="sm" />33 {{ copied() ? 'Copied!' : 'Copy' }}34 </button>35 </div>36 </div>37 38 <!-- Code block -->39 <ai-code-block40 [code]="code()"41 [language]="language()"42 [showLineNumbers]="showLineNumbers()"43 />44 </div>45 `,46})47export class CustomCodeBlockComponent {48 code = input.required<string>();49 language = input('typescript');50 showLineNumbers = input(true);51 52 run = output<string>();53 54 // Runnable languages55 isRunnable = computed(() => {56 const runnable = ['javascript', 'typescript', 'python'];57 return runnable.includes(this.language());58 });59 60 // Copy state61 private _copied = signal(false);62 copied = this._copied.asReadonly();63 64 copyCode() {65 navigator.clipboard.writeText(this.code());66 this._copied.set(true);67 setTimeout(() => this._copied.set(false), 2000);68 }69}Custom Input Component
Add model selection and character count
Extend the chat input with model selection and other features:
1// custom-input.component.ts2import { Component, input, output, signal, computed } from '@angular/core';3import { FormsModule } from '@angular/forms';4import { ChatInputComponent } from '@angular-ai-kit/core';5import { HlmSelectImports } from '@angular-ai-kit/spartan-ui/select';6 7interface Model {8 id: string;9 name: string;10 description: string;11}12 13@Component({14 selector: 'app-custom-input',15 imports: [FormsModule, ChatInputComponent, HlmSelectImports],16 template: `17 <div class="custom-input-container">18 <!-- Model selector -->19 <div class="flex items-center gap-2 mb-2">20 <label class="text-sm text-muted-foreground">Model:</label>21 <select22 hlmSelect23 [(ngModel)]="selectedModel"24 class="w-48"25 >26 @for (model of models(); track model.id) {27 <option [value]="model.id">{{ model.name }}</option>28 }29 </select>30 </div>31 32 <!-- Main input -->33 <ai-chat-input34 [placeholder]="placeholder()"35 [disabled]="disabled()"36 [maxLength]="maxLength()"37 (messageSubmit)="handleSubmit($event)"38 />39 40 <!-- Character count -->41 <div class="flex justify-between mt-1 text-xs text-muted-foreground">42 <span>{{ charCount() }} / {{ maxLength() }}</span>43 <span>Using: {{ selectedModelName() }}</span>44 </div>45 </div>46 `,47})48export class CustomInputComponent {49 placeholder = input('Type your message...');50 disabled = input(false);51 maxLength = input(4000);52 53 models = input<Model[]>([54 { id: 'gpt-4', name: 'GPT-4', description: 'Most capable' },55 { id: 'gpt-3.5', name: 'GPT-3.5', description: 'Fast & cheap' },56 { id: 'claude-3', name: 'Claude 3', description: 'Best for writing' },57 ]);58 59 messageSubmit = output<{ content: string; model: string }>();60 61 selectedModel = signal('gpt-4');62 charCount = signal(0);63 64 selectedModelName = computed(() => {65 const model = this.models().find(m => m.id === this.selectedModel());66 return model?.name ?? 'Unknown';67 });68 69 handleSubmit(content: string) {70 this.messageSubmit.emit({71 content,72 model: this.selectedModel(),73 });74 }75}Configuration via DI Tokens
Global configuration using dependency injection
Provide global configuration using Angular's dependency injection:
1// Customize via DI tokens2import {3 AI_AVATAR_CONFIG,4 AI_CODE_HIGHLIGHT_CONFIG,5 AI_MARKDOWN_CONFIG,6} from '@angular-ai-kit/core';7 8@Component({9 providers: [10 {11 provide: AI_AVATAR_CONFIG,12 useValue: {13 userAvatar: '/avatars/user.png',14 assistantAvatar: '/avatars/bot.png',15 showAvatar: true,16 size: 'md',17 },18 },19 {20 provide: AI_CODE_HIGHLIGHT_CONFIG,21 useValue: {22 theme: 'github-dark',23 showLineNumbers: true,24 wrapLongLines: false,25 },26 },27 {28 provide: AI_MARKDOWN_CONFIG,29 useValue: {30 sanitize: true,31 linkTarget: '_blank',32 enableGfm: true,33 },34 },35 ],36})37export class ChatModule {}Available Tokens
AI_AVATAR_CONFIG- Avatar settingsAI_CODE_HIGHLIGHT_CONFIG- Code highlightingAI_MARKDOWN_CONFIG- Markdown renderingAI_THEME_CONFIG- Theme settings
Best Practices
Tips for effective customization
Start Simple
- Use
customClassesfirst - Then CSS variables for theming
- Create wrappers for complex needs
Stay Consistent
- Use your design system tokens
- Create reusable wrapper components
- Document your customizations
Test Thoroughly
- Test in light and dark mode
- Test responsive behavior
- Test accessibility (keyboard, screen readers)
Keep Updated
- Avoid relying on internal classes
- Use public APIs only
- Check changelog on updates