The CometChatMessageList component displays a real-time list of messages for the active conversation. It is a composite component that effectively manages real-time operations and includes various types of messages such as Text Messages, Media Messages, Stickers, and more.The component follows a Hybrid Approach architecture where:
MessageListService handles all SDK interactions, state management, and real-time updates
Component @Input properties allow developers to override service behavior for flexibility
Both service methods and @Input properties are available, with @Input taking priority when provided
To fetch messages for a specific entity, you need to provide either a User or Group object. The component will not display any messages without one of these inputs.
The following parameters in messagesRequestBuilder will always be overridden by the component:
This section provides a complete reference of all @Input properties, @Output events, and public methods available in the CometChatMessageList component.
The CalendarObject interface is used for date format configuration:
interface CalendarObject { today?: string; // Format for today's date (e.g., 'Today' or 'hh:mm A') yesterday?: string; // Format for yesterday's date (e.g., 'Yesterday') otherDays?: string; // Format for other dates (e.g., 'DD MMM, YYYY')}
Example: Custom Date Formats
const customDateFormat: CalendarObject = { today: 'Today at hh:mm A', yesterday: 'Yesterday at hh:mm A', otherDays: 'MMM DD, YYYY'};// In template<cometchat-message-list [user]="user" [separatorDateTimeFormat]="customDateFormat"></cometchat-message-list>
enum MessageBubbleAlignment { left = 'left', // Message bubble aligned to the left right = 'right', // Message bubble aligned to the right center = 'center' // Message bubble centered (used for action messages)}
The CometChatMessageList component provides extensive customization options to match your application’s design and functionality requirements. This section covers all customization approaches available.
The simplest way to customize the message list is through component @Input properties. These allow you to control behavior and appearance for a specific component instance.
import { Component } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent, MessageListAlignment } from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-left-aligned-chat', standalone: true, imports: [CometChatMessageListComponent], template: ` <!-- Standard alignment: sent messages right, received messages left --> <cometchat-message-list [user]="user" [messageAlignment]="'standard'" ></cometchat-message-list> <!-- Left alignment: all messages aligned to the left --> <cometchat-message-list [user]="user" [messageAlignment]="'left'" ></cometchat-message-list> `})export class LeftAlignedChatComponent { user?: CometChat.User;}
For global customization that applies to all message list instances, use the MessageBubbleConfigService. This service allows you to configure message bubble views globally or per message type.
import { inject } from '@angular/core';import { MessageBubbleConfigService } from '@cometchat/chat-uikit-angular';export class ConfigManagementComponent { private bubbleConfigService = inject(MessageBubbleConfigService); // Clear all configurations (type-specific and global) resetAllCustomizations(): void { this.bubbleConfigService.clearAll(); } // Clear configuration for a specific message type resetTextMessageConfig(): void { this.bubbleConfigService.clearType('text_message'); } // Clear only global views (keep type-specific) resetGlobalViews(): void { this.bubbleConfigService.clearGlobalViews(); }}
Multiple Message Lists with Different Configurations
By default, MessageBubbleConfigService and FormatterConfigService are provided at the root level (providedIn: 'root'), meaning all message list instances share the same configuration. This works well for most applications.However, if you need different bubble styles or formatters for different message lists (e.g., a main chat panel and a thread panel side by side), you can scope these services to a wrapper component using Angular’s hierarchical dependency injection.
Each <cometchat-message-list> already gets its own MessageListService instance automatically (the component provides it internally). The scoping technique below applies only to customization services like MessageBubbleConfigService and FormatterConfigService.
import { Component, TemplateRef, ViewChild, AfterViewInit, inject } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent, CometChatMessageHeaderComponent, CometChatMessageComposerComponent, MessageBubbleConfigService, FormatterConfigService,} from '@cometchat/chat-uikit-angular';// Wrapper component that scopes its own service instances@Component({ selector: 'app-thread-panel', standalone: true, imports: [ CometChatMessageListComponent, CometChatMessageHeaderComponent, CometChatMessageComposerComponent, ], // Providing services here creates NEW instances for this component and its children providers: [MessageBubbleConfigService, FormatterConfigService], template: ` <div class="thread-panel"> <cometchat-message-header [user]="user" [group]="group"></cometchat-message-header> <cometchat-message-list [user]="user" [group]="group" [parentMessageId]="parentMessageId"></cometchat-message-list> <cometchat-message-composer [user]="user" [group]="group" [parentMessageId]="parentMessageId"></cometchat-message-composer> </div> <ng-template #minimalStatusInfo let-context> <span class="thread-status">{{ context.message.getSentAt() * 1000 | date:'shortTime' }}</span> </ng-template> `,})export class ThreadPanelComponent implements AfterViewInit { @ViewChild('minimalStatusInfo') minimalStatusInfo!: TemplateRef<any>; @Input() user?: CometChat.User; @Input() group?: CometChat.Group; @Input() parentMessageId?: number; // This injects the LOCAL instance, not the root singleton private bubbleConfig = inject(MessageBubbleConfigService); ngAfterViewInit(): void { // This only affects the message list inside THIS wrapper this.bubbleConfig.setGlobalView('statusInfoView', this.minimalStatusInfo); }}
The main panel’s MessageBubbleConfigService customizations (set at the root level) do not affect the thread panel, and vice versa. Angular’s DI hierarchy ensures each panel resolves its own service instance.
Services you can scope this way:
Service
What it customizes
MessageBubbleConfigService
Bubble templates per message type
FormatterConfigService
Text formatters (mentions, URLs, custom)
CometChatTemplatesService
Shared and component-specific list templates (loading, empty, error, header, footer)
RichTextEditorService
Rich text editor configuration
Do not scope ChatStateService — it is intentionally a singleton that tracks the app-wide active conversation. Scoping it would break cross-component state synchronization.
Text formatters allow you to process and transform message text content. They can detect patterns like mentions, URLs, hashtags, or custom patterns and apply formatting.
Text formatters extend the CometChatTextFormatter base class:
import { CometChat } from '@cometchat/chat-sdk-javascript';abstract class CometChatTextFormatter { /** Unique identifier for this formatter */ abstract readonly id: string; /** Priority (lower = earlier in pipeline). Default: 100 */ priority: number = 100; /** Get the regex pattern for detecting formattable content */ abstract getRegex(): RegExp; /** Format the input text */ abstract format(text: string): string; /** Check if this formatter should process the text (default: true) */ shouldFormat(text: string, message?: CometChat.BaseMessage): boolean; /** Get metadata extracted during formatting */ getMetadata(): Record<string, unknown>; /** Reset formatter state */ reset(): void;}
Formatters are applied in order of their priority property (lower numbers run first). Each formatter receives the output of the previous formatter as its input.
Message options are the actions available in the context menu when interacting with a message (edit, delete, reply, forward, etc.). You can customize which options are shown using the hide* input properties.
CometChat UIKit uses CSS variables for styling, making it easy to customize the appearance without modifying component code. Override these variables in your global styles or scoped to specific components.
Automatically match the user’s system theme preference:
import { Component, OnInit, OnDestroy } from '@angular/core';@Component({ selector: 'app-auto-theme', template: `<ng-content></ng-content>`})export class AutoThemeComponent implements OnInit, OnDestroy { private mediaQuery?: MediaQueryList; private listener?: (e: MediaQueryListEvent) => void; ngOnInit(): void { // Check for system dark mode preference this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); // Set initial theme this.applyTheme(this.mediaQuery.matches); // Listen for changes this.listener = (e) => this.applyTheme(e.matches); this.mediaQuery.addEventListener('change', this.listener); } ngOnDestroy(): void { if (this.mediaQuery && this.listener) { this.mediaQuery.removeEventListener('change', this.listener); } } private applyTheme(isDark: boolean): void { if (isDark) { document.documentElement.setAttribute('data-theme', 'dark'); } else { document.documentElement.removeAttribute('data-theme'); } }}
Use CSS variables consistently throughout your application to ensure theme changes propagate correctly. Avoid hardcoding color values in component styles.
CometChatMessageList supports two usage patterns for receiving the active user or group context.
Using Service
Using Props
When used alongside cometchat-conversations, the message list automatically subscribes to ChatStateService. No explicit [user] or [group] input is needed — the component loads messages for the active conversation.
import { Component } from '@angular/core';import { CometChatConversationsComponent, CometChatMessageHeaderComponent, CometChatMessageListComponent, CometChatMessageComposerComponent,} from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-chat', standalone: true, imports: [ CometChatConversationsComponent, CometChatMessageHeaderComponent, CometChatMessageListComponent, CometChatMessageComposerComponent, ], template: ` <div class="chat-layout"> <cometchat-conversations (itemClick)="onConversationClick($event)" ></cometchat-conversations> <div class="chat-panel"> <cometchat-message-header></cometchat-message-header> <!-- Automatically loads messages for the active conversation --> <cometchat-message-list></cometchat-message-list> <cometchat-message-composer></cometchat-message-composer> </div> </div> `,})export class ChatComponent { onConversationClick(conversation: any): void {}}
This is the recommended approach. The message list stays in sync with the conversation list without manual wiring.
Pass [user] or [group] directly to control which conversation’s messages are displayed. This overrides ChatStateService state.
import { Component } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent } from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-chat', standalone: true, imports: [CometChatMessageListComponent], template: ` <cometchat-message-list [user]="selectedUser" ></cometchat-message-list> `,})export class ChatComponent { selectedUser!: CometChat.User;}
Thread view allows users to have focused conversations around a specific message. When a user clicks on a message’s thread indicator, you can display the thread replies in a separate view.
Reactions allow users to respond to messages with emoji. The message list component provides built-in support for displaying and interacting with reactions.
import { Component, OnInit } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent } from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-chat-with-reactions', standalone: true, imports: [CometChatMessageListComponent], template: ` @if (user) { <cometchat-message-list [user]="user" [hideReactionOption]="false" (reactionClick)="onReactionClick($event)" (reactionListItemClick)="onReactionListItemClick($event)" (error)="onError($event)" ></cometchat-message-list> } `})export class ChatWithReactionsComponent implements OnInit { user?: CometChat.User; async ngOnInit(): Promise<void> { try { this.user = await CometChat.getUser('RECEIVER_UID'); } catch (error) { console.error('Error fetching user:', error); } } /** * Called when a reaction emoji is clicked on a message. * Use this to toggle the reaction (add if not present, remove if present). */ onReactionClick(event: { reaction: CometChat.ReactionCount; message: CometChat.BaseMessage }): void { const { reaction, message } = event; console.log('Reaction clicked:', reaction.getReaction(), 'on message:', message.getId()); // The component handles adding/removing reactions automatically // You can add custom logic here if needed } /** * Called when a specific user's reaction is clicked in the reaction list. * Use this to show user details or navigate to their profile. */ onReactionListItemClick(event: { reaction: CometChat.Reaction; message: CometChat.BaseMessage }): void { const { reaction, message } = event; console.log('User reaction clicked:', reaction.getReaction(), 'by:', reaction.getReactedBy()); } onError(error: CometChat.CometChatException): void { console.error('Message list error:', error); }}
import { Component, OnInit } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent } from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-programmatic-reactions', standalone: true, imports: [CometChatMessageListComponent], template: ` @if (user) { <cometchat-message-list [user]="user" [hideReactionOption]="false" (reactionClick)="handleReactionToggle($event)" ></cometchat-message-list> } `})export class ProgrammaticReactionsComponent implements OnInit { user?: CometChat.User; async ngOnInit(): Promise<void> { try { this.user = await CometChat.getUser('RECEIVER_UID'); } catch (error) { console.error('Error:', error); } } /** * Toggle a reaction on a message. * If the user has already reacted with this emoji, remove it. * Otherwise, add the reaction. */ async handleReactionToggle(event: { reaction: CometChat.ReactionCount; message: CometChat.BaseMessage }): Promise<void> { const { reaction, message } = event; const emoji = reaction.getReaction(); const messageId = message.getId(); try { // Check if user has already reacted with this emoji const hasReacted = reaction.getReactedByMe(); if (hasReacted) { // Remove the reaction await CometChat.removeReaction(messageId, emoji); console.log('Reaction removed:', emoji); } else { // Add the reaction await CometChat.addReaction(messageId, emoji); console.log('Reaction added:', emoji); } } catch (error) { console.error('Error toggling reaction:', error); } } /** * Add a specific reaction to a message. */ async addReaction(messageId: number, emoji: string): Promise<void> { try { await CometChat.addReaction(messageId, emoji); console.log('Reaction added successfully'); } catch (error) { console.error('Error adding reaction:', error); } } /** * Remove a specific reaction from a message. */ async removeReaction(messageId: number, emoji: string): Promise<void> { try { await CometChat.removeReaction(messageId, emoji); console.log('Reaction removed successfully'); } catch (error) { console.error('Error removing reaction:', error); } }}
The message list component automatically updates the UI when reactions are added or removed. You don’t need to manually refresh the message list after reaction operations.
Smart replies provide AI-generated response suggestions based on the last received message. They appear after a configurable delay when the message contains trigger keywords.
import { Component, OnInit, ViewChild } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent, CometChatMessageComposerComponent} from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-smart-replies-chat', standalone: true, imports: [CometChatMessageListComponent, CometChatMessageComposerComponent], template: ` <div class="chat-container"> @if (user) { <cometchat-message-list #messageList [user]="user" [showSmartReplies]="true" [smartRepliesKeywords]="smartRepliesKeywords" [smartRepliesDelayDuration]="smartRepliesDelay" (smartReplyClick)="onSmartReplyClick($event)" (error)="onError($event)" ></cometchat-message-list> <cometchat-message-composer [user]="user" (messageSent)="onMessageSent()" (typing)="onTyping()" ></cometchat-message-composer> } </div> `, styles: [` .chat-container { display: flex; flex-direction: column; height: 100vh; } cometchat-message-list { flex: 1; overflow: hidden; } `]})export class SmartRepliesChatComponent implements OnInit { @ViewChild('messageList') messageList!: CometChatMessageListComponent; user?: CometChat.User; /** * Keywords that trigger smart reply suggestions. * Smart replies appear when the last received message contains any of these keywords. */ smartRepliesKeywords = ['what', 'when', 'why', 'who', 'where', 'how', '?', 'help', 'can you']; /** * Delay in milliseconds before showing smart replies. * Default is 10000ms (10 seconds). */ smartRepliesDelay = 5000; // 5 seconds async ngOnInit(): Promise<void> { try { this.user = await CometChat.getUser('RECEIVER_UID'); } catch (error) { console.error('Error fetching user:', error); } } /** * Called when a smart reply suggestion is clicked. * Send the selected reply as a message. */ async onSmartReplyClick(reply: string): Promise<void> { console.log('Smart reply selected:', reply); if (!this.user) return; try { // Create and send the text message const textMessage = new CometChat.TextMessage( this.user.getUid(), reply, CometChat.RECEIVER_TYPE.USER ); await CometChat.sendMessage(textMessage); console.log('Smart reply sent successfully'); // Notify the message list that a message was sent // This hides the smart replies this.messageList.onMessageSent(); } catch (error) { console.error('Error sending smart reply:', error); } } /** * Called when user starts typing. * Hides smart replies while user is composing a message. */ onTyping(): void { this.messageList.onUserTyping(); } /** * Called when a message is sent. * Hides smart replies after sending. */ onMessageSent(): void { this.messageList.onMessageSent(); } onError(error: CometChat.CometChatException): void { console.error('Error:', error); }}
Conversation starters provide AI-generated suggestions to help users begin a conversation. They appear when the conversation is empty (no messages yet).
import { Component, OnInit, ViewChild } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent, CometChatMessageComposerComponent} from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-conversation-starters-chat', standalone: true, imports: [CometChatMessageListComponent, CometChatMessageComposerComponent], template: ` <div class="chat-container"> @if (user) { <cometchat-message-list #messageList [user]="user" [showConversationStarters]="true" (conversationStarterClick)="onConversationStarterClick($event)" (error)="onError($event)" ></cometchat-message-list> <cometchat-message-composer [user]="user" ></cometchat-message-composer> } </div> `, styles: [` .chat-container { display: flex; flex-direction: column; height: 100vh; } cometchat-message-list { flex: 1; overflow: hidden; } `]})export class ConversationStartersChatComponent implements OnInit { @ViewChild('messageList') messageList!: CometChatMessageListComponent; user?: CometChat.User; async ngOnInit(): Promise<void> { try { this.user = await CometChat.getUser('RECEIVER_UID'); } catch (error) { console.error('Error fetching user:', error); } } /** * Called when a conversation starter is clicked. * Send the selected starter as the first message. */ async onConversationStarterClick(starter: string): Promise<void> { console.log('Conversation starter selected:', starter); if (!this.user) return; try { // Create and send the text message const textMessage = new CometChat.TextMessage( this.user.getUid(), starter, CometChat.RECEIVER_TYPE.USER ); await CometChat.sendMessage(textMessage); console.log('Conversation starter sent successfully'); // Notify the message list that a message was sent // This hides the conversation starters this.messageList.onMessageSent(); } catch (error) { console.error('Error sending conversation starter:', error); } } onError(error: CometChat.CometChatException): void { console.error('Error:', error); }}
Appear after the configured delay when the last received message contains trigger keywords
Hidden when the user starts typing
Hidden when a message is sent
Display up to 4 AI-generated suggestions
Conversation Starters:
Appear only when the conversation is empty (no messages)
Hidden once any message is sent or received
Display up to 4 AI-generated suggestions
AI Smart Chat Features require the CometChat AI extension to be enabled in your CometChat dashboard. Without the extension, smart replies and conversation starters will not appear even when enabled in the component.
For the best user experience, consider adjusting the smartRepliesDelayDuration based on your use case. A shorter delay (3-5 seconds) works well for support chat, while a longer delay (10-15 seconds) may be better for casual conversations.
The CometChatMessageList component is designed with accessibility in mind, following WCAG 2.1 Level AA guidelines. This section covers keyboard navigation, ARIA attributes, screen reader support, and best practices for creating accessible chat experiences.
The message list component provides comprehensive keyboard support for users who navigate without a mouse. All interactive elements are keyboard accessible.
// When a context menu is open:// - Arrow Up/Down: Navigate between options// - Enter/Space: Select the focused option// - Escape: Close the menu and return focus to the message// - Home: Jump to first option// - End: Jump to last option
Always test keyboard navigation by unplugging your mouse and navigating through the entire chat interface using only the keyboard. Ensure all actions can be completed without a pointing device.
<!-- The message list uses role="list" for the container --><div class="cometchat-message-list__messages" role="list" aria-label="Chat messages" aria-live="polite" aria-relevant="additions"> <!-- Each message is a list item --> <div class="cometchat-message-bubble" role="listitem" [attr.aria-label]="getMessageAriaLabel(message)"> <!-- Message content --> </div></div>
Testing with screen readers is essential to ensure the message list is accessible to users with visual impairments. This section provides guidance for testing with popular screen readers.
1. Download and install NVDA from https://www.nvaccess.org/2. Press Ctrl+Alt+N to start NVDA3. Navigate to your chat application4. Use these NVDA commands: - Insert+Down Arrow: Read from current position - Insert+Up Arrow: Read current line - Tab: Move to next focusable element - Shift+Tab: Move to previous focusable element - Insert+F7: Show elements list (links, headings, etc.) - Insert+Space: Toggle forms mode (for interactive elements) - Escape: Exit current context
1. Press Cmd+F5 to enable VoiceOver2. Navigate to your chat application3. Use these VoiceOver commands: - VO+Right Arrow: Move to next element (VO = Ctrl+Option) - VO+Left Arrow: Move to previous element - VO+Space: Activate current element - VO+Shift+Down Arrow: Interact with element - VO+Shift+Up Arrow: Stop interacting - VO+U: Open rotor (navigation menu) - Escape: Close menus/dialogs
1. Go to Settings > Accessibility > VoiceOver2. Enable VoiceOver3. Use these gestures: - Swipe Right: Move to next element - Swipe Left: Move to previous element - Double Tap: Activate current element - Two-finger Swipe Up: Read from top - Two-finger Swipe Down: Read from current position - Three-finger Swipe: Scroll - Escape gesture (two-finger Z): Go back/close
1. Go to Settings > Accessibility > TalkBack2. Enable TalkBack3. Use these gestures: - Swipe Right: Move to next element - Swipe Left: Move to previous element - Double Tap: Activate current element - Swipe Up then Right: Next navigation setting - Swipe Down then Right: Previous navigation setting - Two-finger Swipe: Scroll - Back button: Go back/close
Ensure sufficient color contrast for all text and interactive elements:
/* Minimum contrast ratios (WCAG 2.1 Level AA) *//* Normal text: 4.5:1 *//* Large text (18px+ or 14px+ bold): 3:1 *//* UI components and graphics: 3:1 */:root { /* These default colors meet contrast requirements */ --cometchat-text-color-primary: #141414; /* On white: 16.1:1 ✓ */ --cometchat-text-color-secondary: #727272; /* On white: 4.9:1 ✓ */ --cometchat-text-color-tertiary: #A1A1A1; /* On white: 2.8:1 - use for large text only */ /* Primary color on white background */ --cometchat-primary-color: #6852D6; /* On white: 4.6:1 ✓ */}/* Dark theme also meets contrast requirements */[data-theme="dark"] { --cometchat-text-color-primary: #FFFFFF; /* On dark: 15.3:1 ✓ */ --cometchat-text-color-secondary: #989898; /* On dark: 6.2:1 ✓ */}
Before releasing your chat implementation, verify:
Perceivable
All images have appropriate alt text
Color is not the only means of conveying information
Text has sufficient contrast (4.5:1 for normal, 3:1 for large)
Content is readable when zoomed to 200%
Operable
All functionality is keyboard accessible
Focus order is logical and intuitive
Focus indicators are visible
No keyboard traps exist
Users can pause, stop, or hide moving content
Understandable
Language is specified (lang attribute)
Error messages are clear and helpful
Labels and instructions are provided
Navigation is consistent
Robust
Valid HTML is used
ARIA attributes are used correctly
Content works with assistive technologies
Component works across different browsers
Accessibility is not a one-time task. Regularly test your implementation with real users who rely on assistive technologies, and incorporate their feedback into your development process.
Consider hiring accessibility consultants or conducting usability testing with users who have disabilities. Automated tools catch only about 30% of accessibility issues—manual testing is essential.
The CometChatMessageList component is designed for optimal performance, even with large message histories. This section covers optimization strategies, memory management, and performance benchmarks to help you build efficient chat experiences.
Configure lazy loading for images and videos to improve initial render time:
@Component({ template: ` <ng-template #customImageContent let-context> <img [src]="context.message.getAttachment()?.getUrl()" loading="lazy" decoding="async" alt="Image attachment" /> </ng-template> `})export class LazyLoadMediaComponent { // Images load only when they enter the viewport}
Use browser developer tools to monitor memory usage:
Chrome DevTools Memory Profiling:1. Open DevTools (F12)2. Go to Memory tab3. Take heap snapshot before and after actions4. Compare snapshots to identify memory leaksKey metrics to monitor:- JS Heap Size: Should stabilize, not continuously grow- DOM Nodes: Should remain relatively constant during scroll- Event Listeners: Should not accumulate over time
Performance varies based on message complexity (text vs. media), device capabilities, and browser. These benchmarks represent typical scenarios with mixed message types.
Performance Tab Analysis:1. Open DevTools (F12) → Performance tab2. Click Record, perform actions, click Stop3. Analyze the flame chart for: - Long tasks (> 50ms) - may cause jank - Layout thrashing - multiple forced reflows - Excessive paint operationsKey areas to check:- Scripting time: Should be < 50% of total- Rendering time: Should be < 30% of total- Painting time: Should be < 20% of total- Idle time: Higher is better
Performance optimization is an ongoing process. Regularly profile your application, especially after adding new features or updating dependencies. Set performance budgets and monitor them in your CI/CD pipeline.
Avoid premature optimization. Focus on the most impactful optimizations first (pagination, lazy loading, change detection) before micro-optimizing. Always measure before and after changes to verify improvements.
This section provides comprehensive, copy-paste ready code examples for common use cases with the CometChatMessageList component. Each example includes complete setup code, imports, and detailed comments explaining the implementation.
This example demonstrates the complete setup process for integrating CometChatMessageList into your Angular application, including CometChat initialization, user login, and component configuration.
Complete Setup
main.ts
Group Chat
Minimal Setup
/** * Complete CometChat Message List Setup Example * * This example demonstrates: * 1. CometChat SDK initialization * 2. User authentication (login) * 3. Fetching a user/group for conversation * 4. Configuring the message list component * 5. Handling events and errors * * Prerequisites: * - CometChat account with App ID and Auth Key * - @cometchat/chat-sdk-javascript installed * - @cometchat/chat-uikit-angular installed * * @see Requirements 10.1 - Basic setup example * @see Requirements 10.8 - Copy-paste ready */// ============================================================// Step 1: Application Entry Point (main.ts)// ============================================================import { bootstrapApplication } from '@angular/platform-browser';import { AppComponent } from './app/app.component';import { appConfig } from './app/app.config';import { CometChatUIKit, UIKitSettingsBuilder } from '@cometchat/chat-uikit-angular';/** * CometChat Configuration * Replace these values with your actual CometChat credentials * Get these from: https://app.cometchat.com */const COMETCHAT_CONFIG = { APP_ID: 'YOUR_APP_ID', // Your CometChat App ID REGION: 'YOUR_REGION', // 'us', 'eu', 'in', etc. AUTH_KEY: 'YOUR_AUTH_KEY', // Your Auth Key (dev only — use Auth Token in production)};const UIKitSettings = new UIKitSettingsBuilder() .setAppId(COMETCHAT_CONFIG.APP_ID) .setRegion(COMETCHAT_CONFIG.REGION) .setAuthKey(COMETCHAT_CONFIG.AUTH_KEY) .subscribePresenceForAllUsers() .build();CometChatUIKit.init(UIKitSettings) .then(() => { console.log('✅ CometChat UIKit initialized successfully'); bootstrapApplication(AppComponent, appConfig).catch(console.error); }) .catch((error) => { console.error('❌ CometChat UIKit initialization failed:', error); });// ============================================================// app.config.ts// ============================================================import { ApplicationConfig } from '@angular/core';import { provideRouter } from '@angular/router';export const appConfig: ApplicationConfig = { providers: [ provideRouter([]), ],};// ============================================================// Step 2: Chat Component (chat.component.ts)// ============================================================import { Component, OnInit, OnDestroy, signal, computed } from '@angular/core';import { CommonModule } from '@angular/common';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent, CometChatMessageHeaderComponent, CometChatMessageComposerComponent,} from '@cometchat/chat-uikit-angular';/** * Chat Component * * A complete chat interface with: * - Message header showing user/group info * - Message list displaying conversation messages * - Message composer for sending new messages */@Component({ selector: 'app-chat', standalone: true, imports: [ CommonModule, CometChatMessageListComponent, CometChatMessageHeaderComponent, CometChatMessageComposerComponent, ], template: ` <!-- Loading State --> @if (isLoading()) { <div class="chat-loading"> <div class="chat-loading__spinner"></div> <p class="chat-loading__text">Loading chat...</p> </div> } <!-- Error State --> @if (error()) { <div class="chat-error"> <p class="chat-error__message">{{ error() }}</p> <button class="chat-error__retry" (click)="retrySetup()"> Retry </button> </div> } <!-- Chat Interface --> @if (isReady() && chatUser()) { <div class="chat-container"> <!-- Message Header: Shows user info and back button --> <cometchat-message-header [user]="chatUser()!" (backClick)="onBackClick()" ></cometchat-message-header> <!-- Message List: Displays conversation messages --> <cometchat-message-list [user]="chatUser()!" [scrollToBottomOnNewMessages]="true" (threadRepliesClick)="onThreadClick($event)" (error)="onMessageListError($event)" ></cometchat-message-list> <!-- Message Composer: Input for sending messages --> <cometchat-message-composer [user]="chatUser()!" (error)="onComposerError($event)" ></cometchat-message-composer> </div> } `, styles: [` /* Container Layout */ .chat-container { display: flex; flex-direction: column; height: 100vh; width: 100%; background: var(--cometchat-background-color-01, #ffffff); } /* Message list takes remaining space */ cometchat-message-list { flex: 1; overflow: hidden; } /* Loading State Styles */ .chat-loading { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; gap: var(--cometchat-spacing-3, 12px); } .chat-loading__spinner { width: 40px; height: 40px; border: 3px solid var(--cometchat-border-color-light, #e8e8e8); border-top-color: var(--cometchat-primary-color, #6852D6); border-radius: 50%; animation: spin 1s linear infinite; } .chat-loading__text { font: var(--cometchat-font-body-regular); color: var(--cometchat-text-color-secondary, #727272); } @keyframes spin { to { transform: rotate(360deg); } } /* Error State Styles */ .chat-error { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; gap: var(--cometchat-spacing-4, 16px); padding: var(--cometchat-spacing-4, 16px); } .chat-error__message { font: var(--cometchat-font-body-regular); color: var(--cometchat-error-color, #F44649); text-align: center; } .chat-error__retry { padding: var(--cometchat-spacing-2, 8px) var(--cometchat-spacing-4, 16px); background: var(--cometchat-primary-color, #6852D6); color: var(--cometchat-static-white, #ffffff); border: none; border-radius: var(--cometchat-radius-2, 8px); font: var(--cometchat-font-button-medium); cursor: pointer; } .chat-error__retry:hover { opacity: 0.9; } `],})export class ChatComponent implements OnInit, OnDestroy { // ==================== State Management ==================== /** Current chat user for 1-on-1 conversation */ chatUser = signal<CometChat.User | null>(null); /** Loading state during setup */ isLoading = signal<boolean>(true); /** Error message if setup fails */ error = signal<string | null>(null); /** Computed: Check if chat is ready to display */ isReady = computed(() => !this.isLoading() && !this.error()); // ==================== Configuration ==================== /** * User ID to chat with * Replace with the actual user ID you want to chat with */ private readonly RECEIVER_UID = 'RECEIVER_USER_ID'; // ==================== Lifecycle ==================== async ngOnInit(): Promise<void> { await this.setupChat(); } ngOnDestroy(): void { // Cleanup: Remove any listeners if needed } // ==================== Setup Methods ==================== /** * Complete chat setup process * 1. Check if user is already logged in * 2. If not, login the user * 3. Fetch the receiver user */ async setupChat(): Promise<void> { this.isLoading.set(true); this.error.set(null); try { // Step 1: Check if already logged in let loggedInUser = await CometChatUIKit.getLoggedinUser(); // Step 2: Login if not already logged in if (!loggedInUser) { loggedInUser = await this.loginUser(); } console.log('✅ User logged in:', loggedInUser.getName()); // Step 3: Fetch the user to chat with const receiverUser = await CometChat.getUser(this.RECEIVER_UID); this.chatUser.set(receiverUser); console.log('✅ Chat ready with:', receiverUser.getName()); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Setup failed'; this.error.set(errorMessage); console.error('❌ Chat setup failed:', err); } finally { this.isLoading.set(false); } } /** * Login user via CometChatUIKit * * IMPORTANT: In production, use a secure authentication method: * - Generate auth tokens on your server * - Use CometChatUIKit.loginWithAuthToken(authToken) instead */ private async loginUser(): Promise<CometChat.User> { const uid = 'CURRENT_USER_ID'; // Replace with actual user ID return await CometChatUIKit.login(uid); } /** * Retry setup after an error */ async retrySetup(): Promise<void> { await this.setupChat(); } // ==================== Event Handlers ==================== /** * Handle back button click from message header * Navigate back to conversation list or close chat */ onBackClick(): void { console.log('Back button clicked'); // Implement navigation logic here // Example: this.router.navigate(['/conversations']); } /** * Handle thread replies click * Open thread view for the selected message */ onThreadClick(message: CometChat.BaseMessage): void { console.log('Thread clicked for message:', message.getId()); // Implement thread view navigation // Example: this.openThreadPanel(message); } /** * Handle message list errors * Log and optionally display to user */ onMessageListError(error: CometChat.CometChatException): void { console.error('Message list error:', error); // Optionally show a toast notification } /** * Handle composer errors * Log and optionally display to user */ onComposerError(error: CometChat.CometChatException): void { console.error('Composer error:', error); // Optionally show a toast notification }}
/** * Application Entry Point (main.ts) * * Initialize CometChat UIKit before bootstrapping the Angular app. * This is the recommended pattern — init must resolve before any * UIKit components are rendered. */import { bootstrapApplication } from '@angular/platform-browser';import { appConfig } from './app/app.config';import { AppComponent } from './app/app.component';import { CometChatUIKit, UIKitSettingsBuilder } from '@cometchat/chat-uikit-angular';const UIKitSettings = new UIKitSettingsBuilder() .setAppId('YOUR_APP_ID') .setRegion('YOUR_REGION') .setAuthKey('YOUR_AUTH_KEY') // dev only — use loginWithAuthToken() in production .subscribePresenceForAllUsers() .build();CometChatUIKit.init(UIKitSettings) .then(() => { bootstrapApplication(AppComponent, appConfig).catch(console.error); }) .catch((error) => { console.error('CometChat UIKit initialization failed:', error); });
/** * Group Chat Example * * This example shows how to set up the message list for group conversations */import { Component, OnInit, signal } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent, CometChatMessageHeaderComponent, CometChatMessageComposerComponent,} from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-group-chat', standalone: true, imports: [ CometChatMessageListComponent, CometChatMessageHeaderComponent, CometChatMessageComposerComponent, ], template: ` @if (chatGroup()) { <div class="chat-container"> <!-- Message Header for Group --> <cometchat-message-header [group]="chatGroup()!" ></cometchat-message-header> <!-- Message List for Group --> <cometchat-message-list [group]="chatGroup()!" [scrollToBottomOnNewMessages]="true" (error)="onError($event)" ></cometchat-message-list> <!-- Message Composer for Group --> <cometchat-message-composer [group]="chatGroup()!" ></cometchat-message-composer> </div> } `, styles: [` .chat-container { display: flex; flex-direction: column; height: 100vh; } cometchat-message-list { flex: 1; overflow: hidden; } `],})export class GroupChatComponent implements OnInit { /** Current group for conversation */ chatGroup = signal<CometChat.Group | null>(null); /** Group GUID to chat in */ private readonly GROUP_GUID = 'YOUR_GROUP_GUID'; async ngOnInit(): Promise<void> { try { // Fetch the group const group = await CometChat.getGroup(this.GROUP_GUID); this.chatGroup.set(group); console.log('✅ Group chat ready:', group.getName()); } catch (error) { console.error('❌ Failed to fetch group:', error); } } onError(error: CometChat.CometChatException): void { console.error('Chat error:', error); }}
/** * Minimal Setup Example * * The simplest possible implementation of CometChatMessageList * Use this as a starting point and add features as needed */import { Component, OnInit } from '@angular/core';import { CometChat } from '@cometchat/chat-sdk-javascript';import { CometChatMessageListComponent } from '@cometchat/chat-uikit-angular';@Component({ selector: 'app-minimal-chat', standalone: true, imports: [CometChatMessageListComponent], template: ` @if (user) { <cometchat-message-list [user]="user" (error)="onError($event)" ></cometchat-message-list> } `, styles: [` :host { display: block; height: 100vh; } `],})export class MinimalChatComponent implements OnInit { user?: CometChat.User; async ngOnInit(): Promise<void> { // Assumes CometChat is already initialized and user is logged in this.user = await CometChat.getUser('RECEIVER_UID'); } onError(error: CometChat.CometChatException): void { console.error('Error:', error); }}
Security Note: The examples above use Auth Key for simplicity. In production applications:
Generate authentication tokens on your server
Use CometChatUIKit.loginWithAuthToken(authToken) instead of CometChatUIKit.login(uid)
Start with the minimal setup and progressively add features as needed. This approach helps you understand each configuration option and troubleshoot issues more easily.