Skip to content
Skillv1.0.0

cometchat-angular-placement

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

by cometchat(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from cometchat/cometchat-skills (skills/cometchat-angular-placement/SKILL.md). Install upstream with npx skills add cometchat/cometchat-skills --skill cometchat-angular-placement. Copyright stays with the author (MIT).

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:

// chat-pane.component.ts
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.Group;
}

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.

// app.routes.ts
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) },
];
// conversations-page.component.ts
@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-homecometchat-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();
    // reassign (new ref) so OnPush hosts update
    if (e instanceof CometChat.User)  { this.activeUser = e; this.activeGroup = undefined; }
    if (e instanceof CometChat.Group) { this.activeGroup = e; this.activeUser = undefined; }
  }
}

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.

// open from any component:
this.dialog.open(ChatDialogComponent, { width: "420px", height: "640px", data: { user } });

// chat-dialog.component.ts
@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 { /* select handlers route to a thread or open the pane */ }

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

  1. 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.)
  2. No CUSTOM_ELEMENTS_SCHEMA, no NgModule — import each component class into the host standalone component's imports: [].
  3. 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.
  4. 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.
  5. Lazy-load full-page chat routes via loadComponent so the kit bundle isn't in the initial chunk.
  6. Brand/theme via CSS variables (--cometchat-*) — see cometchat-angular-theming, 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

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/cometchat-cometchat-skills-cometchat-angular-placement/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

cometchat-cometchat-skills-cometchat-angular-placement.ocm.jsonjson
{
  "ocm": "1",
  "id": "cometchat-cometchat-skills-cometchat-angular-placement",
  "kind": "skill",
  "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.",
  "publisher": "cometchat",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "general"
    ],
    "tags": [
      "skill-md",
      "cometchat",
      "angular",
      "placement",
      "routing",
      "sidebar",
      "modal",
      "tabs",
      "embedded",
      "layout"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "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."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/cometchat/cometchat-skills",
      "path": "skills/cometchat-angular-placement/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/cometchat/cometchat-skills/blob/HEAD/skills/cometchat-angular-placement/SKILL.md",
      "key": "cometchat/cometchat-skills/skills/cometchat-angular-placement/SKILL.md"
    },
    "compatibility": "Angular >=17 <22; @cometchat/chat-uikit-angular ^5.0",
    "license": "MIT"
  },
  "instructions": "> **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.\n\n## Purpose\n\nThe five canonical placement patterns for chat in an Angular app, each as **v5 standalone-component composition**. Read `cometchat-angular-core` (setup) + `cometchat-angular-components` (catalo",
  "cost": {
    "context_tokens": 4431
  }
}

Fetch it by URL: GET /api/v1/registry/cometchat-cometchat-skills-cometchat-angular-placement/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.