| name | cometchat-angular-placement |
| description | Where to put chat in an Angular UI Kit v5 app โ Route-based, Sidebar, Modal/Dialog, Tab-based, and Embedded placements. Each maps to standalone-component composition (no composites, no NgModule) with Angular Router / Material wiring. |
| license | MIT |
| compatibility | Angular >=17 <22; @cometchat/chat-uikit-angular ^5.0 |
| metadata | {"author":"CometChat","version":"4.0.0","tags":"cometchat angular placement routing sidebar modal tabs embedded layout standalone v5"} |
Ground truth: @cometchat/chat-uikit-angular@5.x standalone components + docs/ui-kit/angular. Official docs: https://www.cometchat.com/docs/ui-kit/angular/overview ยท Docs MCP: claude mcp add --transport http cometchat-docs https://www.cometchat.com/docs/mcp (or fetch the URL directly without MCP). Verify symbols against the installed package/source before relying on them.
Purpose
The five canonical placement patterns for chat in an Angular app, each as v5 standalone-component composition. Read cometchat-angular-core (setup) + cometchat-angular-components (catalog) first.
v5 has no composite components. There is no <cometchat-conversations-with-messages> โ every layout composes the individual standalone components (<cometchat-conversations> + <cometchat-message-header> / -message-list> / -message-composer>). Import each component class into the host standalone component's imports: []. No NgModule, no CUSTOM_ELEMENTS_SCHEMA.
"What are you building?" โ placement recommendation
| User intent | Recommended placement | Experience |
|---|
| Messaging app (WhatsApp / Telegram style) | Route-based โ /conversations โ /messages/:uid | Full-page chat |
| SaaS / marketplace with chat as a feature | Sidebar โ persistent panel alongside main content | Split-pane |
| Support app or focused 1-to-1 | Route-based (single thread) โ straight into one chat | Single thread |
| Full messaging hub with calls / users / groups | Tab-based โ Chats / Users / Groups / Calls tabs | Tabbed messenger |
| Occasional chat overlay from a non-chat screen | Modal/Dialog โ Angular Material MatDialog / CDK overlay | Modal |
| Chat embedded inside an existing page section | Embedded โ components inside a parent layout | Inline |
Visual reference
1. Route-based (full page) 2. Sidebar (split-pane)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโ
โ โ Hiking Group โฎ โ โ Convos โ โ Group โฎ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ (messages) โ โ Hiking โ (messages) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ Alice โ โโโโโโโโโโโโโโโโ
โ + Type a messageโฆ โถ โ โ Bob โ Type message โถ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโ
3. Modal/Dialog 4. Tab-based 5. Embedded
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
โ Chat w/ Alice โ โ โ Chats Users Groups โ โ Product details โ
โโโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโโโโโโค
โ (messages) โ โ (active tab) โ โ Chat with seller โ
โ Type message โถ โ โ โ โ [header/list/composer]โ
โโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ
All five compose the same primitives โ only the container/routing differs.
A shared two-pane chat component
Most placements reuse one composed chat surface. Define it once as a standalone component:
import { Component, Input } from "@angular/core";
import {
CometChatMessageHeaderComponent,
CometChatMessageListComponent,
CometChatMessageComposerComponent,
} from "@cometchat/chat-uikit-angular";
import { CometChat } from "@cometchat/chat-sdk-javascript";
@Component({
selector: "app-chat-pane",
standalone: true,
imports: [
CometChatMessageHeaderComponent,
CometChatMessageListComponent,
CometChatMessageComposerComponent,
],
template: `
<ng-container *ngIf="user || group; else empty">
<cometchat-message-header [user]="user" [group]="group"></cometchat-message-header>
<cometchat-message-list [user]="user" [group]="group"></cometchat-message-list>
<cometchat-message-composer [user]="user" [group]="group"></cometchat-message-composer>
</ng-container>
<ng-template #empty><div class="empty">Select a conversation</div></ng-template>`,
styles: [`
:host { display: flex; flex-direction: column; height: 100%; }
cometchat-message-header, cometchat-message-composer { flex-shrink: 0; }
cometchat-message-list { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
`],
})
export class ChatPaneComponent {
@Input() user?: CometChat.User;
@Input() group?: CometChat.;
}
Two ways to drive the active conversation (both verified against the kit source)
<cometchat-message-header> / -message-list> / -message-composer> each expose optional @Input() user? and @Input() group?. They also inject the kit's ChatStateService (exported from @cometchat/chat-uikit-angular) and auto-subscribe to its activeUser() / activeGroup() signals when no input is bound. So:
- State-service mode (canonical V5 default โ what the docs and sample app use). Render the three message components with no
[user]/[group] bindings. When <cometchat-conversations> has no (itemClick) handler bound, clicking a row auto-calls ChatStateService.setActiveConversation(conv), which the message components react to. You only read state for layout gating, e.g. @if (chatState.activeUser() || chatState.activeGroup()). To drive it yourself (a thread route, a deep link), call chatState.setActiveUser(user) / setActiveGroup(group) โ these are mutually exclusive (setting one clears the other).
- Props mode (explicit control). Pass
user XOR group down as inputs (shown in app-chat-pane above). Use this when you bind your own (itemClick) handler and route/own the selection โ see the next note.
The app-chat-pane above uses props mode so it's reusable across routes/modals/embeds. For a single-host sidebar that matches the docs verbatim, drop the inputs and let ChatStateService wire it.
Load-bearing kit behavior: <cometchat-conversations> only auto-sets ChatStateService when (itemClick) is not observed. The source is literally if (this.itemClick.observed) { this.itemClick.emit(conv); } else { this.chatStateService.setActiveConversation(conv); }. The moment you bind (itemClick), the auto-wiring stops โ you must then drive the view yourself (props, chatState.setActive*, or router.navigate). <cometchat-users> / <cometchat-groups> behave the same way with setActiveUser / setActiveGroup.
The kit's <cometchat-*> elements are display: inline by default, so the list won't flex or scroll without the host contract above. Two things are load-bearing: (1) the host is a flex column with height: 100%, and (2) each custom element is itself made a flex column โ cometchat-message-list needs flex: 1; min-height: 0; overflow: hidden (the min-height: 0 is what lets it shrink and scroll inside the flex parent; omit it and the list overflows instead of scrolling). The app's index.html/global styles must also set html, body { height: 100% } so the height: 100% chain has a root to resolve against.
1. Route-based placement
Two routes: a conversation list and a thread. Lazy-load both as standalone components.
export const routes: Routes = [
{ path: "conversations", loadComponent: () =>
import("./conversations-page.component").then(m => m.ConversationsPageComponent) },
{ path: "messages/:uid", loadComponent: () =>
import("./messages-page.component").then(m => m.MessagesPageComponent) },
];
@Component({
standalone: true,
imports: [CometChatConversationsComponent],
template: `<cometchat-conversations (itemClick)="open($event)"></cometchat-conversations>`,
})
export class ConversationsPageComponent {
constructor(private router: Router) {}
open(c: CometChat.Conversation) {
const e = c.getConversationWith();
if (e instanceof CometChat.User) this.router.navigate(["/messages", e.getUid()]);
}
}
messages-page.component.ts reads :uid, resolves the CometChat.User (via CometChat.getUser(uid)), and renders <app-chat-pane [user]="user">.
Binding (itemClick) here is deliberate: it suppresses the kit's auto-setActiveConversation (you want a navigation, not an in-place swap) and hands you the conversation to route with. The thread page then uses props mode (or calls chatState.setActiveUser) since the list and the thread live on different routes.
2. Sidebar placement (split-pane)
One standalone component, conversations on the left, the chat surface on the right. This is the layout the docs and the kit's own sample app (cometchat-home โ cometchat-selector + cometchat-messages) ship โ both use state-service mode.
Recommended โ state-service auto-wiring (docs/sample-app verbatim). No (itemClick), no inputs, no manual reassignment. <cometchat-conversations> auto-sets ChatStateService on click and the message components auto-subscribe. Import inject from @angular/core and ChatStateService + the four component classes from @cometchat/chat-uikit-angular:
@Component({
selector: "app-chat-sidebar",
standalone: true,
imports: [
CometChatConversationsComponent,
CometChatMessageHeaderComponent,
CometChatMessageListComponent,
CometChatMessageComposerComponent,
],
template: `
<div class="split">
<aside><cometchat-conversations></cometchat-conversations></aside>
<main>
@if (chatState.activeUser() || chatState.activeGroup()) {
<cometchat-message-header></cometchat-message-header>
<cometchat-message-list></cometchat-message-list>
<cometchat-message-composer></cometchat-message-composer>
} @else {
<div class="empty">Select a conversation</div>
}
</main>
</div>`,
styles: [`
.split { display: flex; height: 100%; }
aside { width: 320px; flex-shrink: 0; border-right: 1px solid var(--cometchat-border-color-light); overflow: hidden; display: flex; flex-direction: column; }
aside cometchat-conversations { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
main cometchat-message-header, main cometchat-message-composer { flex-shrink: 0; }
main cometchat-message-list { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
`],
})
export class ChatSidebarComponent {
chatState = inject(ChatStateService);
}
Alternative โ props mode (explicit control). Bind (itemClick) and drive app-chat-pane yourself. Binding (itemClick) suppresses the auto-wiring (see the load-bearing note above), so you own the selection:
@Component({
selector: "app-chat-sidebar",
standalone: true,
imports: [CometChatConversationsComponent, ChatPaneComponent],
template: `
<div class="split">
<aside><cometchat-conversations (itemClick)="select($event)"></cometchat-conversations></aside>
<main><app-chat-pane [user]="activeUser" [group]="activeGroup"></app-chat-pane></main>
</div>`,
styles: [`.split{display:flex;height:100%} aside{width:320px;flex-shrink:0;border-right:1px solid var(--cometchat-border-color-light)} main{flex:1;min-width:0}`],
})
export class ChatSidebarComponent {
activeUser?: CometChat.User; activeGroup?: CometChat.Group;
select(c: CometChat.Conversation) {
const e = c.getConversationWith();
if (e instanceof CometChat.User) { this.activeUser = e; this.activeGroup = undefined; }
if (e instanceof CometChat.Group) { this.activeGroup = e; this. = ; }
}
}
3. Modal/Dialog placement (Angular Material)
Render the composed chat pane inside a MatDialog. Never a composite โ there isn't one, and a 3-panel composite wouldn't fit a modal anyway.
this.dialog.open(ChatDialogComponent, { width: "420px", height: "640px", data: { user } });
@Component({
standalone: true,
imports: [ChatPaneComponent],
template: `<app-chat-pane [user]="data.user"></app-chat-pane>`,
})
export class ChatDialogComponent {
constructor(@Inject(MAT_DIALOG_DATA) public data: { user: CometChat.User }) {}
}
(For an inbox-in-modal, drop <cometchat-conversations> above the pane in a two-pane dialog body.)
4. Tab-based placement
A standalone shell with Material tabs (or your own) over Conversations / Users / Groups / Call Logs.
@Component({
standalone: true,
imports: [MatTabsModule, CometChatConversationsComponent, CometChatUsersComponent,
CometChatGroupsComponent, CometChatCallLogsComponent, ChatPaneComponent],
template: `
<mat-tab-group>
<mat-tab label="Chats"><div class="tab-pane"><cometchat-conversations (itemClick)="select($event)"></cometchat-conversations></div></mat-tab>
<mat-tab label="Users"><div class="tab-pane"><cometchat-users (itemClick)="selectUser($event)"></cometchat-users></div></mat-tab>
<mat-tab label="Groups"><div class="tab-pane"><cometchat-groups (itemClick)="selectGroup($event)"></cometchat-groups></div></mat-tab>
<mat-tab label="Calls"><div class="tab-pane"><cometchat-call-logs></cometchat-call-logs></div></mat-tab>
</mat-tab-group>`,
styles: [`
:host { display: flex; flex-direction: column; height: 100%; }
mat-tab-group { flex: 1; min-height: 0; }
.tab-pane { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.tab-pane > * { flex: 1; min-height: 0; overflow: hidden; display: flex; flex-direction: column; }
`],
})
export class ChatTabsComponent { }
MatTabsModule is a standalone-importable Angular Material module โ add it to imports: [] alongside the CometChat components.
Selection wiring is the same trade-off as the sidebar. If you want the kit to auto-route a click into the active conversation (the docs' tab example does this), drop the (itemClick) handlers โ <cometchat-conversations> auto-calls setActiveConversation and <cometchat-users> auto-calls setActiveUser, then mount the message components in a side/center pane gated on chatState.activeUser()/activeGroup(). Bind (itemClick) only when you want to route to a separate page or otherwise own the selection.
The list components inside each <mat-tab> need the same flex-host treatment as the chat pane โ a Material tab body has no implicit height, so without a flex .tab-pane wrapper (and the elements made flex: 1; min-height: 0; overflow: hidden) the inline <cometchat-*> lists collapse to zero height and won't scroll.
5. Embedded placement
Drop <app-chat-pane> (or the raw header/list/composer) inside an existing page, scoped to a known user/group.
@Component({
standalone: true,
imports: [ChatPaneComponent],
template: `
<section class="product">โฆproduct detailsโฆ</section>
<section class="seller-chat" style="height:480px">
<h3>Chat with seller</h3>
<app-chat-pane [user]="seller"></app-chat-pane>
</section>`,
})
export class ProductPageComponent { seller!: CometChat.User; }
Give the embedded container an explicit height โ the message list fills its parent.
Hard rules
- No composites in v5.
<cometchat-conversations-with-messages> / -users-with-messages> / -groups-with-messages> do not exist โ compose <cometchat-conversations> + the message components. (If you've seen these in v4 docs, they were removed.)
- No
CUSTOM_ELEMENTS_SCHEMA, no NgModule โ import each component class into the host standalone component's imports: [].
- Drive the active chat one of two ways โ don't mix them on the same surface. Either (a) state-service mode (canonical V5 default): no
[user]/[group], no (itemClick) โ <cometchat-conversations>/-users>/-groups> auto-call ChatStateService.setActive* and the message components auto-subscribe; gate layout on chatState.activeUser()/activeGroup(). Or (b) props mode: pass user XOR group on header/list/composer (reassign a new reference on change so OnPush hosts re-render). Binding (itemClick) suppresses the auto-wiring (kit source: itemClick.observed ? emit : setActiveConversation) โ once bound, you own selection via props, chatState.setActive*, or routing.
- Give chat containers an explicit height AND make the custom elements flex. The kit's
<cometchat-*> elements are display: inline by default โ they will not flex or scroll on their own. The host must be a flex column with a resolvable height: 100% (so set html, body { height: 100% } globally), and each element must itself be display: flex; flex-direction: column. cometchat-message-list needs flex: 1; min-height: 0; overflow: hidden โ without min-height: 0 it overflows the viewport instead of scrolling; header/composer take flex-shrink: 0. With no height or no element-flex, the list collapses or fails to scroll.
- Lazy-load full-page chat routes via
loadComponent so the kit bundle isn't in the initial chunk.
- Brand/theme via CSS variables (
--cometchat-*) โ see , not a theme service.
Skill routing reference
cometchat-angular-core โ setup, init, login, ChatStateService lifecycle
cometchat-angular-components โ component catalog + bindings (@Input user/group, itemClick)
cometchat-angular-patterns โ routing, guards, lazy loading, NgZone, SSR
cometchat-angular-theming โ CSS-variable theming