{"id":"3487761e-c923-444d-a6e5-1c5d3ee7a00f","shortId":"8Lkc6b","kind":"skill","title":"cometchat-angular-placement","tagline":"Where to put chat in an Angular app — Route-based, Sidebar, Modal/Dialog, Tab-based, and Embedded placements. Maps each to CometChat component composition with Angular Router wiring and layout patterns.","description":"## Purpose\n\nTeaches Claude the five canonical placement patterns for putting chat inside an Angular app. Each pattern specifies:\n\n1. Which CometChat components to compose\n2. How to wire the placement into Angular Router or Angular Material\n3. Layout gotchas (flex containers, height constraints, z-index)\n4. When to choose this placement over the alternatives\n\n**Read `cometchat-angular-core` and `cometchat-angular-components` before this skill** — the init/login lifecycle and component catalog are prerequisites.\n\nGround truth: `docs/ui-kit/angular/getting-started`, `docs/ui-kit/angular/multi-tab-chat-ui-guide`, and `@cometchat/chat-uikit-angular@4.x` composite components.\n\n---\n\n## \"What are you building?\" — placement recommendation\n\n| User intent | Recommended placement | Experience |\n|---|---|---|\n| Messaging app (WhatsApp / Telegram style) | **Route-based** — `/conversations` → `/messages/:uid` | Full-page chat inside the app |\n| SaaS / marketplace with chat as a feature | **Sidebar** — persistent chat panel alongside main content | Split-pane layout |\n| Support app or focused 1-to-1 | **Route-based (single thread)** — no conversation list, go straight into one chat | Single thread |\n| Full messaging hub with calls / users / groups | **Tab-based** — Chats / Users / Groups / Calls tabs | Tab-based messenger |\n| Occasional chat overlay from a non-chat screen | **Modal/Dialog** — Angular Material `MatDialog` or CDK overlay | Modal |\n| Chat embedded inside an existing page section | **Embedded** — CometChat components inside a parent layout | Inline |\n\n---\n\n## Visual reference — five Angular placement patterns\n\n### 1. Route-based (full page)\n\n```\n┌─────────────────────────────────────────┐\n│ ← Hiking Group                    ⋮     │  ← cometchat-message-header\n├─────────────────────────────────────────┤\n│                                         │\n│              (messages)                 │  ← cometchat-message-list\n│                                         │\n├─────────────────────────────────────────┤\n│ +  Type a message...               ▶    │  ← cometchat-message-composer\n└─────────────────────────────────────────┘\n```\n\n### 2. Sidebar (split-pane)\n\n```\n┌──────────────┬──────────────────────────┐\n│ Conversations│ ← Hiking Group      ⋮    │\n│ ─────────────│ ─────────────────────────│\n│ Hiking Group │                          │\n│ Alice        │      (messages)          │\n│ Bob          │                          │\n│              │ ─────────────────────────│\n│              │ Type a message...    ▶   │\n└──────────────┴──────────────────────────┘\n```\n\n### 3. Modal/Dialog\n\n```\n              ┌──────────────────────┐\n              │ Chat with Alice   ✕  │\n              ├──────────────────────┤\n              │                      │\n              │     (messages)       │\n              │                      │\n              ├──────────────────────┤\n              │ Type message...  ▶   │\n              └──────────────────────┘\n  (page content dimmed behind)\n```\n\n### 4. Tab-based\n\n```\n┌─────────────────────────────────────────┐\n│  Chats  Users  Groups  Calls            │  ← tab bar\n├─────────────────────────────────────────┤\n│                                         │\n│         (active tab content)            │\n│                                         │\n└─────────────────────────────────────────┘\n```\n\n### 5. Embedded (inside an existing page)\n\n```\n┌─────────────────────────────────────────┐\n│ Product details                         │\n│ [product image + specs]                 │\n├─────────────────────────────────────────┤\n│ Chat with seller                        │\n│ ┌─────────────────────────────────────┐ │\n│ │ cometchat-message-header            │ │\n│ │ cometchat-message-list              │ │  ← embedded chat\n│ │ cometchat-message-composer          │ │\n│ └─────────────────────────────────────┘ │\n└─────────────────────────────────────────┘\n```\n\n---\n\n## 1. Route-based placement\n\nThe most common pattern — chat lives in its own route, navigated via Angular Router.\n\n### Pattern A — Conversations list → Messages (two routes)\n\n```typescript\n// app-routing.module.ts\nimport { Routes } from \"@angular/router\";\nimport { ConversationsComponent } from \"./conversations/conversations.component\";\nimport { MessagesComponent } from \"./messages/messages.component\";\n\nexport const routes: Routes = [\n  { path: \"conversations\", component: ConversationsComponent },\n  { path: \"messages/user/:uid\", component: MessagesComponent },\n  { path: \"messages/group/:guid\", component: MessagesComponent },\n  { path: \"\", redirectTo: \"conversations\", pathMatch: \"full\" },\n];\n```\n\n```typescript\n// conversations.component.ts\nimport { Component } from \"@angular/core\";\nimport { Router } from \"@angular/router\";\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\nimport { CometChatConversations } from \"@cometchat/chat-uikit-angular\";\nimport { CUSTOM_ELEMENTS_SCHEMA } from \"@angular/core\";\n\n@Component({\n  selector: \"app-conversations\",\n  standalone: true,\n  imports: [CometChatConversations],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <div style=\"height: 100vh; display: flex; flex-direction: column;\">\n      <cometchat-conversations\n        [onItemClick]=\"handleConvClick\"\n        style=\"flex: 1; overflow: hidden;\"\n      ></cometchat-conversations>\n    </div>\n  `,\n})\nexport class ConversationsComponent {\n  constructor(private router: Router) {}\n\n  handleConvClick = (conversation: CometChat.Conversation): void => {\n    const entity = conversation.getConversationWith();\n    const type = conversation.getConversationType();\n    if (type === \"user\") {\n      this.router.navigate([\"/messages/user\", (entity as CometChat.User).getUid()]);\n    } else {\n      this.router.navigate([\"/messages/group\", (entity as CometChat.Group).getGuid()]);\n    }\n  };\n}\n```\n\n```typescript\n// messages.component.ts\nimport { Component, OnInit } from \"@angular/core\";\nimport { ActivatedRoute, Router } from \"@angular/router\";\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\nimport {\n  CometChatMessageHeader,\n  CometChatMessageList,\n  CometChatMessageComposer,\n} from \"@cometchat/chat-uikit-angular\";\nimport { CommonModule } from \"@angular/common\";\nimport { CUSTOM_ELEMENTS_SCHEMA } from \"@angular/core\";\n\n@Component({\n  selector: \"app-messages\",\n  standalone: true,\n  imports: [CommonModule, CometChatMessageHeader, CometChatMessageList, CometChatMessageComposer],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <div style=\"height: 100vh; display: flex; flex-direction: column;\">\n      <cometchat-message-header\n        [user]=\"selectedUser\"\n        [group]=\"selectedGroup\"\n        [onBack]=\"goBack\"\n        [hideBackButton]=\"false\"\n      ></cometchat-message-header>\n      <cometchat-message-list\n        [user]=\"selectedUser\"\n        [group]=\"selectedGroup\"\n        style=\"flex: 1; overflow: hidden;\"\n      ></cometchat-message-list>\n      <cometchat-message-composer\n        [user]=\"selectedUser\"\n        [group]=\"selectedGroup\"\n      ></cometchat-message-composer>\n    </div>\n  `,\n})\nexport class MessagesComponent implements OnInit {\n  selectedUser: CometChat.User | undefined;\n  selectedGroup: CometChat.Group | undefined;\n\n  constructor(private route: ActivatedRoute, private router: Router) {}\n\n  ngOnInit(): void {\n    const uid = this.route.snapshot.paramMap.get(\"uid\");\n    const guid = this.route.snapshot.paramMap.get(\"guid\");\n\n    if (uid) {\n      CometChat.getUser(uid).then((user) => (this.selectedUser = user));\n    } else if (guid) {\n      CometChat.getGroup(guid).then((group) => (this.selectedGroup = group));\n    }\n  }\n\n  goBack = (): void => {\n    this.router.navigate([\"/conversations\"]);\n  };\n}\n```\n\n### Pattern B — Single thread (no conversation list)\n\nFor support chat, marketplace \"Contact seller\", or any focused 1-to-1 where the target is known in advance.\n\n```typescript\n// support-chat.component.ts\n@Component({\n  selector: \"app-support-chat\",\n  standalone: true,\n  imports: [CommonModule, CometChatMessageHeader, CometChatMessageList, CometChatMessageComposer],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <div *ngIf=\"agent; else loading\" style=\"height: 100vh; display: flex; flex-direction: column;\">\n      <cometchat-message-header [user]=\"agent\"></cometchat-message-header>\n      <cometchat-message-list\n        [user]=\"agent\"\n        style=\"flex: 1; overflow: hidden;\"\n      ></cometchat-message-list>\n      <cometchat-message-composer [user]=\"agent\"></cometchat-message-composer>\n    </div>\n    <ng-template #loading><p>Connecting to support...</p></ng-template>\n  `,\n})\nexport class SupportChatComponent implements OnInit {\n  agent: CometChat.User | undefined;\n\n  ngOnInit(): void {\n    CometChat.getUser(\"support-agent-uid\").then((user) => (this.agent = user));\n  }\n}\n```\n\n---\n\n## 2. Sidebar placement (split-pane)\n\nFor SaaS apps where chat is a persistent panel alongside main content.\n\n```typescript\n// chat-layout.component.ts\n@Component({\n  selector: \"app-chat-layout\",\n  standalone: true,\n  imports: [\n    CommonModule,\n    CometChatConversations,\n    CometChatMessageHeader,\n    CometChatMessageList,\n    CometChatMessageComposer,\n  ],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <div style=\"display: flex; height: 100vh; overflow: hidden;\">\n      <!-- Sidebar: conversation list -->\n      <div style=\"width: 320px; flex-shrink: 0; border-right: 1px solid #e8e8e8; overflow: hidden;\">\n        <cometchat-conversations\n          [onItemClick]=\"handleConvClick\"\n          [activeConversation]=\"activeConversation\"\n          style=\"height: 100%;\"\n        ></cometchat-conversations>\n      </div>\n\n      <!-- Main: message thread -->\n      <div style=\"flex: 1; display: flex; flex-direction: column; overflow: hidden;\">\n        <ng-container *ngIf=\"selectedUser || selectedGroup; else placeholder\">\n          <cometchat-message-header\n            [user]=\"selectedUser\"\n            [group]=\"selectedGroup\"\n            [hideBackButton]=\"true\"\n          ></cometchat-message-header>\n          <cometchat-message-list\n            [user]=\"selectedUser\"\n            [group]=\"selectedGroup\"\n            style=\"flex: 1; overflow: hidden;\"\n          ></cometchat-message-list>\n          <cometchat-message-composer\n            [user]=\"selectedUser\"\n            [group]=\"selectedGroup\"\n          ></cometchat-message-composer>\n        </ng-container>\n        <ng-template #placeholder>\n          <div style=\"flex: 1; display: flex; align-items: center; justify-content: center; color: #727272;\">\n            Select a conversation to start chatting\n          </div>\n        </ng-template>\n      </div>\n    </div>\n  `,\n})\nexport class ChatLayoutComponent {\n  selectedUser: CometChat.User | undefined;\n  selectedGroup: CometChat.Group | undefined;\n  activeConversation: CometChat.Conversation | undefined;\n\n  handleConvClick = (conversation: CometChat.Conversation): void => {\n    this.activeConversation = conversation;\n    const entity = conversation.getConversationWith();\n    if (entity instanceof CometChat.User) {\n      this.selectedUser = entity;\n      this.selectedGroup = undefined;\n    } else {\n      this.selectedGroup = entity as CometChat.Group;\n      this.selectedUser = undefined;\n    }\n  };\n}\n```\n\n### Sidebar layout notes\n\n- The sidebar container needs `overflow: hidden` — `<cometchat-conversations>` fills 100% of its parent.\n- The message area needs `flex: 1; overflow: hidden` so the list fills the remaining space.\n- Pass `[activeConversation]` to `<cometchat-conversations>` to highlight the selected row.\n- `[hideBackButton]=\"true\"` on the header since there's no navigation to go back to.\n\n---\n\n## 3. Modal/Dialog placement\n\nFor occasional chat that doesn't belong in the primary navigation. Use Angular Material `MatDialog` or Angular CDK overlay.\n\n### ⚠️ Critical — never use `<cometchat-conversations-with-messages>` in a modal\n\nThe composite renders a 3-panel layout (Conversations + Messages + Details) and needs **≥ 1024px** of horizontal space. Modals are typically 480–960px wide; the Details panel ends up as empty whitespace and the layout looks broken (one column unused, X close button orphaned). Use the **Two-pane** pattern (Pattern A0) for inbox-in-modal, or the **Granular** 1:1 pattern (Pattern A) for \"Contact seller\"-style direct chat.\n\n### Pattern A0 — Inbox in modal (Two-pane: Conversations + Messages)\n\nFor \"click → open a modal showing the user's inbox + selected thread\" (Slack-in-a-popup style).\n\n```typescript\n// inbox-modal.component.ts\nimport { Component, OnInit } from \"@angular/core\";\nimport { MatDialogRef } from \"@angular/material/dialog\";\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\nimport {\n  CometChatConversations,\n  CometChatMessages,\n} from \"@cometchat/chat-uikit-angular\";\nimport { CUSTOM_ELEMENTS_SCHEMA } from \"@angular/core\";\nimport { CommonModule } from \"@angular/common\";\n\n@Component({\n  selector: \"app-inbox-modal\",\n  standalone: true,\n  imports: [CommonModule, CometChatConversations, CometChatMessages],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <div style=\"display: flex; width: 800px; height: 600px;\">\n      <cometchat-conversations\n        style=\"flex: 0 0 320px; border-right: 1px solid #e5e7eb;\"\n        [activeConversation]=\"activeConversation\"\n        [onItemClick]=\"onConversationClick\"\n      ></cometchat-conversations>\n      <cometchat-messages\n        *ngIf=\"activeUser || activeGroup; else empty\"\n        [user]=\"activeUser\"\n        [group]=\"activeGroup\"\n        style=\"flex: 1 1 auto;\"\n      ></cometchat-messages>\n      <ng-template #empty>\n        <div style=\"flex: 1; display: flex; align-items: center; justify-content: center; color: #9ca3af;\">\n          Select a conversation\n        </div>\n      </ng-template>\n    </div>\n  `,\n})\nexport class InboxModalComponent {\n  activeConversation: CometChat.Conversation | null = null;\n  activeUser: CometChat.User | null = null;\n  activeGroup: CometChat.Group | null = null;\n\n  constructor(public dialogRef: MatDialogRef<InboxModalComponent>) {}\n\n  onConversationClick = (conv: CometChat.Conversation): void => {\n    this.activeConversation = conv;\n    const target = conv.getConversationWith();\n    if (target instanceof CometChat.User) {\n      this.activeUser = target;\n      this.activeGroup = null;\n    } else {\n      this.activeGroup = target as CometChat.Group;\n      this.activeUser = null;\n    }\n  };\n}\n```\n\n```typescript\n// Trigger:\nthis.dialog.open(InboxModalComponent, { panelClass: \"inbox-dialog\" });\n```\n\nWhy this works in modal sizing where the composite doesn't:\n- **No third panel** — Conversations (left) + Messages (right) consumes the entire dialog width.\n- **Explicit flex sizing** — Conversations is fixed-width (`flex: 0 0 320px`), Messages takes the rest (`flex: 1 1 auto`). No empty whitespace.\n- **Empty state handled** — `*ngIf` shows a \"Select a conversation\" placeholder until the user clicks one.\n\n### Pattern A — Angular Material MatDialog (recommended)\n\n```typescript\n// chat-dialog.component.ts\nimport { Component, Inject } from \"@angular/core\";\nimport { MAT_DIALOG_DATA, MatDialogRef } from \"@angular/material/dialog\";\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\nimport {\n  CometChatMessageHeader,\n  CometChatMessageList,\n  CometChatMessageComposer,\n} from \"@cometchat/chat-uikit-angular\";\nimport { CUSTOM_ELEMENTS_SCHEMA } from \"@angular/core\";\n\n@Component({\n  selector: \"app-chat-dialog\",\n  standalone: true,\n  imports: [CometChatMessageHeader, CometChatMessageList, CometChatMessageComposer],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <div style=\"width: 480px; height: 600px; display: flex; flex-direction: column;\">\n      <cometchat-message-header\n        [user]=\"data.user\"\n        [onBack]=\"close\"\n        [hideBackButton]=\"false\"\n      ></cometchat-message-header>\n      <cometchat-message-list\n        [user]=\"data.user\"\n        style=\"flex: 1; overflow: hidden;\"\n      ></cometchat-message-list>\n      <cometchat-message-composer [user]=\"data.user\"></cometchat-message-composer>\n    </div>\n  `,\n})\nexport class ChatDialogComponent {\n  constructor(\n    public dialogRef: MatDialogRef<ChatDialogComponent>,\n    @Inject(MAT_DIALOG_DATA) public data: { user: CometChat.User }\n  ) {}\n\n  close = (): void => this.dialogRef.close();\n}\n```\n\n```typescript\n// Trigger from any component:\nimport { MatDialog } from \"@angular/material/dialog\";\n\n@Component({ /* ... */ })\nexport class ProductComponent {\n  constructor(private dialog: MatDialog) {}\n\n  openChat(sellerUid: string): void {\n    CometChat.getUser(sellerUid).then((user) => {\n      this.dialog.open(ChatDialogComponent, {\n        data: { user },\n        panelClass: \"chat-dialog\",\n        disableClose: false,\n      });\n    });\n  }\n}\n```\n\n### Pattern B — Angular CDK Overlay (no Material dependency)\n\n```typescript\nimport { Overlay, OverlayRef } from \"@angular/cdk/overlay\";\nimport { ComponentPortal } from \"@angular/cdk/portal\";\n\n@Component({ /* ... */ })\nexport class TriggerComponent {\n  private overlayRef: OverlayRef | null = null;\n\n  constructor(private overlay: Overlay) {}\n\n  openChat(): void {\n    this.overlayRef = this.overlay.create({\n      hasBackdrop: true,\n      positionStrategy: this.overlay.position().global().centerHorizontally().centerVertically(),\n    });\n    const portal = new ComponentPortal(ChatDialogComponent);\n    this.overlayRef.attach(portal);\n    this.overlayRef.backdropClick().subscribe(() => this.overlayRef?.dispose());\n  }\n}\n```\n\n---\n\n## 4. Tab-based placement\n\nFor full-featured messengers with distinct entry points per content type. Use Angular Material `MatTabGroup` or a custom tab bar.\n\n```typescript\n// chat-tabs.component.ts\nimport { Component } from \"@angular/core\";\nimport { MatTabsModule } from \"@angular/material/tabs\";\nimport {\n  CometChatConversations,\n  CometChatUsers,\n  CometChatGroups,\n  CometChatCallLogs,\n} from \"@cometchat/chat-uikit-angular\";\nimport { CUSTOM_ELEMENTS_SCHEMA } from \"@angular/core\";\n\n@Component({\n  selector: \"app-chat-tabs\",\n  standalone: true,\n  imports: [MatTabsModule, CometChatConversations, CometChatUsers, CometChatGroups, CometChatCallLogs],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <mat-tab-group style=\"height: 100vh;\" animationDuration=\"0ms\">\n      <mat-tab label=\"Chats\">\n        <cometchat-conversations\n          [onItemClick]=\"handleConvClick\"\n          style=\"height: calc(100vh - 48px);\"\n        ></cometchat-conversations>\n      </mat-tab>\n      <mat-tab label=\"Users\">\n        <cometchat-users\n          [onItemClick]=\"handleUserClick\"\n          style=\"height: calc(100vh - 48px);\"\n        ></cometchat-users>\n      </mat-tab>\n      <mat-tab label=\"Groups\">\n        <cometchat-groups\n          [onItemClick]=\"handleGroupClick\"\n          style=\"height: calc(100vh - 48px);\"\n        ></cometchat-groups>\n      </mat-tab>\n      <mat-tab label=\"Calls\">\n        <cometchat-call-logs\n          style=\"height: calc(100vh - 48px);\"\n        ></cometchat-call-logs>\n      </mat-tab>\n    </mat-tab-group>\n  `,\n})\nexport class ChatTabsComponent {\n  handleConvClick = (conversation: CometChat.Conversation): void => { /* navigate */ };\n  handleUserClick = (user: CometChat.User): void => { /* navigate */ };\n  handleGroupClick = (group: CometChat.Group): void => { /* navigate */ };\n}\n```\n\n### Tab wiring notes\n\n- `animationDuration=\"0ms\"` prevents the tab content from fading in/out, which can cause CometChat components to re-initialize.\n- Each tab's content needs an explicit height — `calc(100vh - 48px)` subtracts the tab bar height (48px for Material default).\n- For the **Calls** tab, `<cometchat-call-logs>` only works when `@cometchat/calls-sdk-javascript` is installed. Omit the Calls tab if the project doesn't use calling.\n\n---\n\n## 5. Embedded placement\n\nChat inside an existing page section, not its own route.\n\n```typescript\n// product-detail.component.ts\n@Component({\n  selector: \"app-product-detail\",\n  standalone: true,\n  imports: [\n    CommonModule,\n    CometChatMessageHeader,\n    CometChatMessageList,\n    CometChatMessageComposer,\n  ],\n  schemas: [CUSTOM_ELEMENTS_SCHEMA],\n  template: `\n    <div class=\"product-page\">\n      <div class=\"product-info\">\n        <!-- product details -->\n      </div>\n\n      <div class=\"chat-section\">\n        <h3>Chat with seller</h3>\n        <div style=\"height: 480px; display: flex; flex-direction: column; border: 1px solid #e8e8e8; border-radius: 8px; overflow: hidden;\">\n          <ng-container *ngIf=\"seller; else loadingChat\">\n            <cometchat-message-header\n              [user]=\"seller\"\n              [hideBackButton]=\"true\"\n            ></cometchat-message-header>\n            <cometchat-message-list\n              [user]=\"seller\"\n              style=\"flex: 1; overflow: hidden;\"\n            ></cometchat-message-list>\n            <cometchat-message-composer [user]=\"seller\"></cometchat-message-composer>\n          </ng-container>\n          <ng-template #loadingChat>\n            <div style=\"flex: 1; display: flex; align-items: center; justify-content: center;\">\n              Loading chat...\n            </div>\n          </ng-template>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class ProductDetailComponent implements OnInit {\n  seller: CometChat.User | undefined;\n\n  ngOnInit(): void {\n    CometChat.getUser(this.product.sellerUid).then((user) => (this.seller = user));\n  }\n}\n```\n\n### Embedded gotchas\n\n- **Fixed height required.** CometChat components fill 100% of their parent. Without a bounded height (`height: 480px` or `flex: 1` inside a flex container), the list collapses to zero height and renders empty.\n- **Overflow hidden on the container.** The inner components have their own scroll — the outer container must not scroll over them.\n- Usually the embedded pattern is the wrong default — prefer a Modal trigger from a button on the page, which gives users a dedicated surface for chatting.\n\n---\n\n## Hard rules\n\nThese apply to ALL placement patterns.\n\n1. **NEVER modify the project's existing router without reading it first.** Understand what's there before adding routes or outlets. Don't replace a user's navigation structure unless they explicitly chose \"demo mode.\"\n\n2. **ALWAYS give CometChat containers a bounded height.** Components fill 100% of their parent. If the parent has no bounded height, components collapse to zero height and look empty. Use `height: 100vh`, `height: calc(100vh - Npx)`, or `flex: 1` inside a flex column.\n\n3. **Pass either `[user]` or `[group]`, never both.** Passing both causes runtime errors. Branch in the template based on which one is set.\n\n4. **Resolve user / group before rendering.** The `[user]` and `[group]` inputs expect `CometChat.User` and `CometChat.Group` instances — not bare UID strings. Fetch via `CometChat.getUser(uid)` / `CometChat.getGroup(guid)` in `ngOnInit` and gate the render on the resolved object with `*ngIf`.\n\n5. **Wire `[onThreadRepliesClick]` if you want threads**, or leave it unwired to keep the thread option hidden. The `[onThreadRepliesClick]` input is an `@Input()` callback — use `[onThreadRepliesClick]=\"myFn\"` (square brackets). See `cometchat-angular-components` § 11 for the full threading pattern.\n\n6. **For modal placements, set an explicit width and height on the dialog container.** Angular Material dialogs don't constrain their content by default — without explicit dimensions, CometChat components may render at 0px.\n\n6a. **Never use `<cometchat-conversations-with-messages>` (or `<cometchat-users-with-messages>` / `<cometchat-groups-with-messages>`) inside a modal, dialog, drawer, or sidebar.** These composites render a 3-panel layout (List + Messages + Details) and need ≥ 1024px of horizontal space. In a 480–960px modal, the Details panel ends up as empty whitespace and the layout looks broken. Use the Two-pane pattern (`<cometchat-conversations>` + `<cometchat-messages>`, see § 3 Pattern A0) for inbox-in-modal, or the Granular pattern (`<cometchat-message-header>` + `-message-list>` + `-message-composer>`, see § 3 Pattern A) for 1:1 chat.\n\n7. **For sidebar placements, use `overflow: hidden` on both the sidebar and message area containers.** CometChat components have internal scroll; the outer containers must not add a second scroll layer.\n\n8. **Never animate a CometChat-containing container with CSS `transform`.** `transform` creates a new stacking context, which reparents `position: fixed` overlays (emoji picker, action sheet, reactions popover) and makes them misalign. Animate `left` / `right` / `top` / `bottom` offsets instead.\n\n---\n\n## Skill routing reference\n\n| Skill | When to route |\n|---|---|\n| `cometchat-angular-core` | Always first — init, login, module setup |\n| `cometchat-angular-components` | For component prop details — always |\n| `cometchat-angular-placement` | This skill — picking + wiring a placement |\n| `cometchat-angular-patterns` | Angular-specific routing, lazy loading, guards |\n| `cometchat-angular-theming` | Customize colors / typography / dark mode |\n| `cometchat-angular-features` | Calls, extensions, AI — the \"add a feature\" flow |\n| `cometchat-angular-customization` | Custom slot views, text formatters, events |\n| `cometchat-angular-production` | Server-side auth tokens |\n| `cometchat-angular-troubleshooting` | Blank chat / height issues / dialog sizing |","tags":["cometchat","angular","placement","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-angular-placement","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-angular-placement","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (25,279 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:48.376Z","embedding":null,"createdAt":"2026-05-07T13:05:07.660Z","updatedAt":"2026-05-18T19:04:48.376Z","lastSeenAt":"2026-05-18T19:04:48.376Z","tsv":"'-1':176,635 '/conversations':142,616 '/conversations/conversations.component':377 '/messages':143 '/messages/group':481 '/messages/messages.component':381 '/messages/user':474 '0':1080,1081,1196,1197 '0ms':1531 '0px':1974 '1':55,174,249,342,450,557,633,691,799,871,989,990,1107,1108,1204,1205,1296,1648,1695,1763,1836,2050,2051 '100':770,862,1683,1808 '100vh':670,1487,1497,1507,1557,1829,1832 '1024px':943,1998 '11':1936 '1px':1086 '2':61,273,722,1798 '3':73,289,903,935,1841,1990,2027,2046 '320px':1082,1198 '4':83,119,301,1411,1864 '480':950,2004 '480px':1692 '48px':1488,1498,1508,1558,1564 '5':314,1589,1902 '6':1942 '6a':1975 '7':2053 '8':2083 '960px':951,2005 'a0':980,1001,2029 'action':2107 'activ':311 'activatedrout':494,582 'activeconvers':766,767,825,882,1089,1090,1116 'activegroup':1098,1104,1124 'activeus':1097,1102,1120 'ad':1780 'add':2078,2186 'advanc':642 'agent':665,682,688,699,708,716 'ai':2184 'alic':283,293 'alongsid':163,737 'altern':91 'alway':1799,2133,2147 'angular':3,11,31,50,68,71,95,100,221,246,359,918,922,1227,1360,1429,1934,1956,2131,2141,2150,2160,2163,2171,2180,2192,2202,2211 'angular-specif':2162 'angular/cdk/overlay':1371 'angular/cdk/portal':1375 'angular/common':511,1057 'angular/core':410,428,492,517,1034,1053,1237,1260,1442,1459 'angular/material/dialog':1038,1244,1331 'angular/material/tabs':1446 'angular/router':373,414,497 'anim':2085,2115 'animationdur':1530 'app':12,51,135,151,171,432,521,648,730,745,1061,1264,1463,1607 'app-chat-dialog':1263 'app-chat-layout':744 'app-chat-tab':1462 'app-convers':431 'app-inbox-mod':1060 'app-messag':520 'app-product-detail':1606 'app-routing.module.ts':369 'app-support-chat':647 'appli':1758 'area':868,2066 'auth':2207 'auto':1109,1206 'b':618,1359 'back':901 'bar':310,1436,1562 'bare':1881 'base':15,20,141,179,201,209,252,304,345,1414,1858 'behind':300 'belong':912 'blank':2213 'bob':285 'border':1084 'border-right':1083 'bottom':2119 'bound':1689,1804,1817 'bracket':1930 'branch':1854 'broken':965,2019 'build':126 'button':971,1743 'calc':1486,1496,1506,1556,1831 'call':196,205,308,1570,1580,1588,2182 'callback':1925 'canon':42 'catalog':110 'caus':1541,1851 'cdk':225,923,1361 'centerhorizont':1398 'centervert':1399 'chat':8,47,148,155,161,189,202,212,218,228,291,305,325,337,351,626,650,732,746,815,908,999,1265,1354,1464,1592,1622,1658,1754,2052,2214 'chat-dialog':1353 'chat-dialog.component.ts':1232 'chat-layout.component.ts':741 'chat-tabs.component.ts':1438 'chatdialogcompon':1307,1349,1404 'chatlayoutcompon':818 'chattabscompon':1511 'choos':86 'chose':1795 'class':454,569,704,817,1114,1306,1334,1378,1510,1660 'claud':39 'click':1011,1223 'close':970,1285,1320 'collaps':1702,1820 'color':2174 'column':676,967,1840 'cometchat':2,27,57,94,99,236,258,263,270,329,333,339,416,444,499,536,548,561,678,684,695,762,780,790,803,1040,1076,1094,1246,1279,1289,1300,1480,1490,1500,1542,1633,1641,1652,1680,1801,1933,1969,2068,2088,2130,2140,2149,2159,2170,2179,2191,2201,2210 'cometchat-angular-compon':98,1932,2139 'cometchat-angular-cor':93,2129 'cometchat-angular-custom':2190 'cometchat-angular-featur':2178 'cometchat-angular-pattern':2158 'cometchat-angular-plac':1,2148 'cometchat-angular-product':2200 'cometchat-angular-them':2169 'cometchat-angular-troubleshoot':2209 'cometchat-contain':2087 'cometchat-convers':443,761,1075,1479 'cometchat-group':1499 'cometchat-messag':1093 'cometchat-message-compos':269,338,560,694,802,1299,1651 'cometchat-message-head':257,328,535,677,779,1278,1632 'cometchat-message-list':262,332,547,683,789,1288,1640 'cometchat-us':1489 'cometchat.conversation':462,826,830,1117,1134,1514 'cometchat.getgroup':607,1888 'cometchat.getuser':598,713,1344,1669,1886 'cometchat.group':484,577,823,849,1125,1153,1524,1878 'cometchat.user':477,574,709,820,840,1121,1144,1319,1519,1665,1876 'cometchat/calls-sdk-javascript':1575 'cometchat/chat-sdk-javascript':418,501,1042,1248 'cometchat/chat-uikit-angular':118,422,507,1047,1254,1453 'cometchatcalllog':1451,1473 'cometchatconvers':420,437,752,1044,1068,1448,1470 'cometchatgroup':1450,1472 'cometchatmessag':1045,1069 'cometchatmessagecompos':505,529,657,755,1252,1272,1616 'cometchatmessagehead':503,527,655,753,1250,1270,1614 'cometchatmessagelist':504,528,656,754,1251,1271,1615 'cometchatus':1449,1471 'common':349 'commonmodul':509,526,654,751,1055,1067,1613 'compon':28,58,101,109,122,237,388,393,398,408,429,489,518,645,742,1031,1058,1234,1261,1327,1332,1376,1440,1460,1543,1604,1681,1716,1806,1819,1935,1970,2069,2142,2144 'componentport':1373,1403 'compos':60,272,341,563,697,805,1302,1654,2044 'composit':29,121,932,1172,1987 'connect':700 'const':383,464,467,588,592,834,1138,1400 'constrain':1961 'constraint':79 'constructor':456,579,1128,1308,1336,1385 'consum':1182 'contact':628,995 'contain':77,773,857,1627,1699,1713,1723,1802,1955,2067,2075,2089,2090 'content':165,298,313,739,1426,1535,1551,1963 'context':2099 'conv':1133,1137 'conv.getconversationwith':1140 'convers':183,278,363,387,402,433,445,461,622,763,812,829,833,938,1008,1077,1112,1178,1190,1218,1481,1513 'conversation.getconversationtype':469 'conversation.getconversationwith':466,836 'conversations.component.ts':406 'conversationscompon':375,389,455 'core':96,2132 'creat':2095 'critic':925 'css':2092 'custom':424,439,513,531,659,757,1049,1071,1256,1274,1434,1455,1475,1618,2173,2193,2194 'dark':2176 'data':1241,1315,1317,1350 'data.user':1283,1293,1304 'dedic':1751 'default':1567,1736,1965 'demo':1796 'depend':1365 'detail':321,940,954,1609,1995,2008,2146 'dialog':1163,1185,1240,1266,1314,1338,1355,1954,1958,1982,2217 'dialogref':1130,1310 'dim':299 'dimens':1968 'direct':675,998 'disableclos':1356 'display':671 'dispos':1410 'distinct':1422 'div':663 'docs/ui-kit/angular/getting-started':115 'docs/ui-kit/angular/multi-tab-chat-ui-guide':116 'doesn':910,1173,1585 'drawer':1983 'e5e7eb':1088 'either':1843 'element':425,440,514,532,660,758,1050,1072,1257,1275,1456,1476,1619 'els':479,604,666,777,845,1099,1149,1630 'embed':22,229,235,315,336,1590,1675,1731 'emoji':2105 'empti':959,1100,1208,1210,1708,1826,2013 'end':956,2010 'entir':1184 'entiti':465,475,482,835,838,842,847 'entri':1423 'error':1853 'event':2199 'exist':232,318,1595,1769 'expect':1875 'experi':133 'explicit':1187,1554,1794,1948,1967 'export':382,453,568,703,816,1113,1305,1333,1377,1509,1659 'extens':2183 'fade':1537 'fals':546,1287,1357 'featur':158,1419,2181,2188 'fetch':1884 'fill':861,877,1682,1807 'first':1774,2134 'five':41,245 'fix':1193,1677,2103 'fixed-width':1192 'flex':76,449,556,672,674,690,798,870,1079,1106,1188,1195,1203,1295,1647,1694,1698,1835,1839 'flex-direct':673 'flow':2189 'focus':173,632 'formatt':2198 'full':146,192,253,404,1418,1939 'full-featur':1417 'full-pag':145 'gate':1893 'getguid':485 'getuid':478 'give':1748,1800 'global':1397 'go':185,900 'goback':544,613 'gotcha':75,1676 'granular':988,2037 'ground':113 'group':198,204,256,280,282,307,541,553,566,610,612,785,795,808,1103,1501,1523,1846,1867,1873 'guard':2168 'guid':397,593,595,606,608,1889 'handl':1212 'handleconvclick':447,460,765,828,1483,1512 'handlegroupclick':1503,1522 'handleuserclick':1493,1517 'hard':1755 'hasbackdrop':1393 'header':260,331,538,680,782,893,1281,1635 'height':78,669,769,1485,1495,1505,1555,1563,1678,1690,1691,1705,1805,1818,1823,1828,1830,1951,2215 'hidden':452,559,693,801,860,873,1298,1650,1710,1918,2059 'hidebackbutton':545,787,889,1286,1638 'highlight':885 'hike':255,279,281 'horizont':945,2000 'hub':194 'imag':323 'implement':571,706,1662 'import':370,374,378,407,411,415,419,423,436,488,493,498,502,508,512,525,653,750,1030,1035,1039,1043,1048,1054,1066,1233,1238,1245,1249,1255,1269,1328,1367,1372,1439,1443,1447,1454,1468,1612 'in/out':1538 'inbox':983,1002,1019,1062,1162,2032 'inbox-dialog':1161 'inbox-in-mod':982,2031 'inbox-modal.component.ts':1029 'inboxmodalcompon':1115,1159 'index':82 'init':2135 'init/login':106 'initi':1547 'inject':1235,1312 'inlin':242 'inner':1715 'input':1874,1921,1924 'insid':48,149,230,238,316,1593,1696,1837,1979 'instal':1577 'instanc':1879 'instanceof':839,1143 'instead':2121 'intent':130 'intern':2071 'issu':2216 'keep':1914 'known':640 'layer':2082 'layout':35,74,169,241,747,853,937,963,1992,2017 'lazi':2166 'leav':1910 'left':1179,2116 'lifecycl':107 'list':184,265,335,364,550,623,686,792,876,1291,1643,1701,1993,2041 'live':352 'load':667,1657,2167 'loadingchat':1631 'login':2136 'look':964,1825,2018 'main':164,738 'make':2112 'map':24 'marketplac':153,627 'mat':1239,1313 'matdialog':223,920,1229,1329,1339 'matdialogref':1036,1131,1242,1311 'materi':72,222,919,1228,1364,1430,1566,1957 'mattabgroup':1431 'mattabsmodul':1444,1469 'may':1971 'messag':134,193,259,261,264,268,271,284,288,294,296,330,334,340,365,522,537,549,562,679,685,696,781,791,804,867,939,1009,1095,1180,1199,1280,1290,1301,1634,1642,1653,1994,2040,2043,2065 'message-compos':2042 'message-list':2039 'messages.component.ts':487 'messages/group':396 'messages/user':391 'messagescompon':379,394,399,570 'messeng':210,1420 'misalign':2114 'modal':227,930,947,985,1004,1014,1063,1168,1739,1944,1981,2006,2034 'modal/dialog':17,220,290,904 'mode':1797,2177 'modifi':1765 'modul':2137 'must':1724,2076 'myfn':1928 'navig':357,898,916,1516,1521,1526,1790 'need':858,869,942,1552,1997 'never':926,1764,1847,1976,2084 'new':1402,2097 'ng':772,1626 'ng-contain':771,1625 'ngif':664,774,1096,1213,1628,1901 'ngoninit':586,711,1667,1891 'non':217 'non-chat':216 'note':854,1529 'npx':1833 'null':1118,1119,1122,1123,1126,1127,1148,1155,1383,1384 'object':1899 'occasion':211,907 'offset':2120 'omit':1578 'onback':543,1284 'onconversationclick':1092,1132 'one':188,966,1224,1861 'oninit':490,572,707,1032,1663 'onitemclick':446,764,1091,1482,1492,1502 'onthreadrepliesclick':1904,1920,1927 'open':1012 'openchat':1340,1389 'option':1917 'orphan':972 'outer':1722,2074 'outlet':1783 'overflow':451,558,692,800,859,872,1297,1649,1709,2058 'overlay':213,226,924,1362,1368,1387,1388,2104 'overlayref':1369,1381,1382 'page':147,233,254,297,319,1596,1746 'pane':168,277,727,977,1007,2024 'panel':162,736,936,955,1177,1991,2009 'panelclass':1160,1352 'parent':240,865,1686,1811,1814 'pass':881,1842,1849 'path':386,390,395,400 'pathmatch':403 'pattern':36,44,53,248,350,361,617,978,979,991,992,1000,1225,1358,1732,1762,1941,2025,2028,2038,2047,2161 'per':1425 'persist':160,735 'pick':2154 'picker':2106 'placehold':778,1219 'placement':4,23,43,66,88,127,132,247,346,724,905,1415,1591,1761,1945,2056,2151,2157 'point':1424 'popov':2110 'popup':1026 'portal':1401,1406 'posit':2102 'positionstrategi':1395 'prefer':1737 'prerequisit':112 'prevent':1532 'primari':915 'privat':457,580,583,1337,1380,1386 'product':320,322,1608,2203 'product-detail.component.ts':1603 'productcompon':1335 'productdetailcompon':1661 'project':1584,1767 'prop':2145 'public':1129,1309,1316 'purpos':37 'put':7,46 're':1546 're-initi':1545 'reaction':2109 'read':92,1772 'recommend':128,131,1230 'redirectto':401 'refer':244,2124 'remain':879 'render':933,1707,1869,1895,1972,1988 'repar':2101 'replac':1786 'requir':1679 'resolv':1865,1898 'rest':1202 'right':1085,1181,2117 'rout':14,140,178,251,344,356,367,371,384,385,581,1601,1781,2123,2128,2165 'route-bas':13,139,177,250,343 'router':32,69,360,412,458,459,495,584,585,1770 'row':888 'rule':1756 'runtim':1852 'saa':152,729 'schema':426,438,441,515,530,533,658,661,756,759,1051,1070,1073,1258,1273,1276,1457,1474,1477,1617,1620 'screen':219 'scroll':1720,1726,2072,2081 'second':2080 'section':234,1597 'see':1931,2026,2045 'select':810,887,1020,1110,1216 'selectedgroup':542,554,567,576,776,786,796,809,822 'selectedus':540,552,565,573,775,784,794,807,819 'selector':430,519,646,743,1059,1262,1461,1605 'seller':327,629,996,1624,1629,1637,1645,1656,1664 'selleruid':1341,1345 'server':2205 'server-sid':2204 'set':1863,1946 'setup':2138 'sheet':2108 'show':1015,1214 'side':2206 'sidebar':16,159,274,723,852,856,1985,2055,2063 'sinc':894 'singl':180,190,619 'size':1169,1189,2218 'skill':104,2122,2125,2153 'skill-cometchat-angular-placement' 'slack':1023 'slack-in-a-popup':1022 'slot':2195 'solid':1087 'source-cometchat' 'space':880,946,2001 'spec':324 'specif':2164 'specifi':54 'split':167,276,726 'split-pan':166,275,725 'squar':1929 'stack':2098 'standalon':434,523,651,748,1064,1267,1466,1610 'start':814 'state':1211 'straight':186 'string':1342,1883 'structur':1791 'style':138,448,555,668,689,768,797,997,1027,1078,1105,1294,1484,1494,1504,1646 'subscrib':1408 'subtract':1559 'support':170,625,649,702,715 'support-agent-uid':714 'support-chat.component.ts':644 'supportchatcompon':705 'surfac':1752 'tab':19,200,206,208,303,309,312,1413,1435,1465,1527,1534,1549,1561,1571,1581 'tab-bas':18,199,207,302,1412 'take':1200 'target':638,1139,1142,1146,1151 'teach':38 'telegram':137 'templat':442,534,662,760,1074,1277,1478,1621,1857 'text':2197 'theme':2172 'third':1176 'this.activeconversation':832,1136 'this.activegroup':1147,1150 'this.activeuser':1145,1154 'this.agent':720 'this.dialog.open':1158,1348 'this.dialogref.close':1322 'this.overlay.create':1392 'this.overlay.position':1396 'this.overlayref':1391,1409 'this.overlayref.attach':1405 'this.overlayref.backdropclick':1407 'this.product.selleruid':1670 'this.route.snapshot.parammap.get':590,594 'this.router.navigate':473,480,615 'this.selectedgroup':611,843,846 'this.selecteduser':602,841,850 'this.seller':1673 'thread':181,191,620,1021,1908,1916,1940 'token':2208 'top':2118 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'transform':2093,2094 'trigger':1157,1324,1740 'triggercompon':1379 'troubleshoot':2212 'true':435,524,652,749,788,890,1065,1268,1394,1467,1611,1639 'truth':114 'two':366,976,1006,2023 'two-pan':975,1005,2022 'type':266,286,295,468,471,1427 'typescript':368,405,486,643,740,1028,1156,1231,1323,1366,1437,1602 'typic':949 'typographi':2175 'uid':144,392,589,591,597,599,717,1882,1887 'undefin':575,578,710,821,824,827,844,851,1666 'understand':1775 'unless':1792 'unus':968 'unwir':1912 'use':917,927,973,1428,1587,1827,1926,1977,2020,2057 'user':129,197,203,306,472,539,551,564,601,603,681,687,698,719,721,783,793,806,1017,1101,1222,1282,1292,1303,1318,1347,1351,1491,1518,1636,1644,1655,1672,1674,1749,1788,1844,1866,1871 'usual':1729 'via':358,1885 'view':2196 'visual':243 'void':463,587,614,712,831,1135,1321,1343,1390,1515,1520,1525,1668 'want':1907 'whatsapp':136 'whitespac':960,1209,2014 'wide':952 'width':1186,1194,1949 'wire':33,64,1528,1903,2155 'without':1687,1771,1966 'work':1166,1573 'wrong':1735 'x':120,969 'z':81 'z-index':80 'zero':1704,1822","prices":[{"id":"690eb0e5-dd9a-4a06-9a54-60adcb095c6d","listingId":"3487761e-c923-444d-a6e5-1c5d3ee7a00f","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T13:05:07.660Z"}],"sources":[{"listingId":"3487761e-c923-444d-a6e5-1c5d3ee7a00f","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-angular-placement","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-angular-placement","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:07.660Z","lastSeenAt":"2026-05-18T19:04:48.376Z"}],"details":{"listingId":"3487761e-c923-444d-a6e5-1c5d3ee7a00f","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-angular-placement","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"dff37281fd0504fc65bc0636dd0671754f180432","skill_md_path":"skills/cometchat-angular-placement/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-angular-placement"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-angular-placement","license":"MIT","description":"Where to put chat in an Angular app — Route-based, Sidebar, Modal/Dialog, Tab-based, and Embedded placements. Maps each to CometChat component composition with Angular Router wiring and layout patterns.","compatibility":"Angular >=12 <=15; @cometchat/chat-uikit-angular ^4; @cometchat/chat-sdk-javascript ^4"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-angular-placement"},"updatedAt":"2026-05-18T19:04:48.376Z"}}