{"id":"da98c6c9-65cc-42bc-ad3f-92a5a0e850d1","shortId":"p3CyLZ","kind":"skill","title":"cometchat-react-calls","tagline":"CometChat Calls SDK integration for web React apps (Vite, CRA, Next.js, React Router, Astro). Covers @cometchat/calls-sdk-javascript install, dual-SDK init (Chat SDK + Calls SDK), getRTCToken, the kit's CometChatIncomingCall / CometChatOutgoingCall / CometChatOngoingCall componen","description":"## ⚠️ STOP — mandatory precondition before any code\n\n**Before writing one line of code, you MUST resolve `mode = ringing | session`.** This decides which entire integration shape you scaffold — they don't share UI, navigation, or surface.\n\n| `mode` | Surface shape | Reference |\n|---|---|---|\n| `ringing` | `CometChatIncomingCall` at root + `CometChatCallButtons` near a user / contact / message header. Recipient's screen rings on incoming call. | `references/ringing-integration.md` |\n| `session` | `/meet/:sessionId` route (or equivalent) + `CometChatCalls.joinSession` on a container `<div>`. No ringing — both parties enter the same session ID. | `references/call-session.md` |\n\n**How to resolve mode (in order):**\n\n1. **Check `.cometchat/config.json` for `mode`** — if set by the `cometchat-calls` dispatcher's Step 3.0, trust it.\n2. **Infer from the user's words** — see `cometchat-calls/SKILL.md` Step 3.0 inference table. Confirm in one line: *\"Got it — setting up Ringing. Say so if you wanted meeting-room URLs instead.\"*\n3. **If still ambiguous (e.g. user said only \"integrate calls\" with no qualifier) — ASK before scaffolding.** Don't default to ringing. Use this prompt **verbatim** — preserve the order, the labels, and the descriptions exactly. Do NOT rephrase. Do NOT swap options. Option 1 is \"Session\"; option 2 is \"Ringing\":\n\n   - **question:** \"What kind of calling experience are you building?\"\n   - **header:** \"Calling mode\"\n   - **multiSelect:** false\n   - **options (display in this exact order — Session FIRST, Ringing SECOND):**\n     1. label: \"Session — meeting / conference room\", description: \"Multiple users join the same session by ID or link. No ringing. Like Google Meet, Zoom, or a Slack huddle.\"\n     2. label: \"Ringing — 1:1 or group calls\", description: \"One user calls another (or a small group). Recipient's device rings; they accept or decline. Like FaceTime or WhatsApp calls.\"\n\n   > **Strict-order rule:** the agent's UI primitive must render option 1 above the option 2. Do not let auto-mode classifiers or your own bias reorder them. Session is shown first because the dashboard team has standardized on this order across CometChat product surfaces; consistency matters more than any subjective \"first option\" preference.\n\n**Do not write `CometChatCallButtons` / `CometChatIncomingCall` / `CometChatOngoingCall` code without a confirmed `mode === \"ringing\"`.** Those components are the kit's implementation of ringing; using them silently locks the integration into ringing-shape even if the user wanted a meeting-room flow.\n\n---\n\n## ⚠️ Call container — must have non-zero dimensions when joinSession fires\n\nThe Calls SDK measures the container `<div>` **synchronously** when `joinSession` runs and throws `Container dimensions and number of tiles must be positive` if width or height is 0. This is the calls equivalent of the chat-layout flex-shrink trap.\n\n**Common bug — `h-full` on a flex child resolves to 0:**\n\n```tsx\n// ✗ WRONG — `h-full` is `height: 100%`, but the parent's height is auto\n//   so 100% of auto = 0. SDK crashes.\n<section className=\"flex-1\">\n  <div ref={containerRef} className=\"h-full w-full\" />\n</section>\n```\n\n**Fix — use a flex chain with `min-h-0` and an explicit fallback:**\n\n```tsx\n// ✓ RIGHT — section is a flex column with min-h-0 (so it can shrink), the\n//   container uses flex-1 to claim remaining space, plus an explicit\n//   `minHeight` safety net for very short viewports.\n<section className=\"relative flex flex-1 min-h-0\">\n  <div\n    ref={containerRef}\n    className=\"flex-1 w-full\"\n    style={{ minHeight: 400 }}\n  />\n</section>\n```\n\nIf you can't use flex (e.g. fixed-height modal), just give the container explicit pixels:\n\n```tsx\n<div\n  ref={containerRef}\n  style={{ width: \"100%\", height: \"calc(100vh - 100px)\" }}\n/>\n```\n\n`minHeight: 0` matters specifically for parent flex containers that house the call container — without it, a flex-column ancestor whose content overflows defaults to `min-height: auto` and the call surface gets squeezed to zero. This is the same trap as `cometchat-react-patterns`'s chat-layout rule, applied to calls.\n\n---\n\n## ⚠️ Next.js / SSR — mandatory bundler config\n\nBoth SDKs ship code that **breaks Next.js's SSR pass**:\n\n- `@cometchat/chat-sdk-javascript` references `window` at module load time\n- `@cometchat/calls-sdk-javascript` (v5) imports Node built-ins (`fs`, `path`) gated by a runtime check that the bundler still tries to statically resolve\n\n`\"use client\"` alone does NOT fix this — Next.js evaluates client components during the initial SSR pass for hydration. You need to defer the SDK imports so they only execute in the browser.\n\n**For Next.js (App Router or Pages Router), apply ALL of these:**\n\n1. **Switch dev/build to webpack** in `package.json` scripts (Turbopack's `fs`/`path` aliasing is fragile in Next 16):\n   ```json\n   {\n     \"scripts\": {\n       \"dev\": \"next dev --webpack\",\n       \"build\": \"next build --webpack\",\n       \"start\": \"next start\"\n     }\n   }\n   ```\n\n2. **Add a webpack `fs` fallback** in `next.config.ts`:\n   ```ts\n   import type { NextConfig } from \"next\";\n   const nextConfig: NextConfig = {\n     webpack: (config, { isServer }) => {\n       if (!isServer) {\n         config.resolve = config.resolve || {};\n         config.resolve.fallback = {\n           ...(config.resolve.fallback || {}),\n           fs: false,\n           path: false,\n         };\n       }\n       return config;\n     },\n   };\n   export default nextConfig;\n   ```\n\n3. **Wrap the CometChatProvider in `next/dynamic({ ssr: false })`** — create a small client wrapper and use it from a server-component layout:\n   ```tsx\n   // app/_components/CometChatGate.tsx\n   \"use client\";\n   import dynamic from \"next/dynamic\";\n   import type { ReactNode } from \"react\";\n\n   const CometChatProvider = dynamic(\n     () => import(\"@/cometchat/CometChatProvider\").then((m) => m.CometChatProvider),\n     { ssr: false, loading: () => <div>Loading…</div> },\n   );\n\n   export function CometChatGate({ children }: { children: ReactNode }) {\n     return <CometChatProvider>{children}</CometChatProvider>;\n   }\n   ```\n\n   **⚠️ Provider placement for Ringing mode — mount at app root, not on a sub-route layout.** If the CometChatProvider registers a global `CallListener` for incoming calls (which it should — see \"Ringing mode listener\" below), it MUST be mounted in the root layout (`app/layout.tsx`). Mounting it on a sub-route layout like `app/meet/layout.tsx` means the listener is only armed while the user is browsing under that sub-route — incoming calls land silently when they're on the home page or any other route, and the caller sees a timeout/rejection.\n\n   ```tsx\n   // app/layout.tsx (server component, root layout)\n   import { CometChatGate } from \"./_components/CometChatGate\";\n   export default function RootLayout({ children }: { children: React.ReactNode }) {\n     return (\n       <html><body>\n         <CometChatGate>{children}</CometChatGate>\n       </body></html>\n     );\n   }\n   ```\n\n   For Session-only mode (no ringing — both parties navigate to a shared `/meet/:id` URL), the sub-route layout is fine — listener isn't load-bearing.\n\n   **Ringing mode listener** — inside `CometChatProvider`, after login:\n   ```ts\n   const { CometChat } = await import(\"@cometchat/chat-sdk-javascript\");\n   CometChat.addCallListener(\"ringing-listener\", new CometChat.CallListener({\n     onIncomingCallReceived: async (call: any) => {\n       const accepted = await CometChat.acceptCall(call.getSessionId());\n       router.push(`/meet/${encodeURIComponent(accepted.getSessionId())}`);\n     },\n     onOutgoingCallAccepted: (call: any) => {\n       router.push(`/meet/${encodeURIComponent(call.getSessionId())}`);\n     },\n     onOutgoingCallRejected: (call: any) => { /* show toast */ },\n     onIncomingCallCancelled: (call: any) => { /* dismiss any UI */ },\n   }));\n   ```\n   The `/meet/:sessionId` page handles `joinSession`. Validated end-to-end against Pixel 3 V6 Android peer on 2026-05-12.\n\n4. **Lazy-load the SDKs inside `init.ts`** — replace top-level static imports with `await import(...)` inside the init/login functions. The provider is gated by step 3, but `init.ts` is shared with any page that uses CometChatCalls directly, so belt-and-braces it:\n   ```ts\n   let chatModule: typeof import(\"@cometchat/chat-sdk-javascript\") | null = null;\n   let callsModule: typeof import(\"@cometchat/calls-sdk-javascript\") | null = null;\n\n   async function loadSdks() {\n     if (!chatModule) chatModule = await import(\"@cometchat/chat-sdk-javascript\");\n     if (!callsModule) callsModule = await import(\"@cometchat/calls-sdk-javascript\");\n     return { CometChat: chatModule.CometChat, CometChatCalls: callsModule.CometChatCalls };\n   }\n   ```\n\n5. **No top-level SDK imports in any page that's reachable via App Router routing.** Inside `useEffect` handlers, dynamic-import the SDK:\n   ```tsx\n   useEffect(() => {\n     let CallsSdk: typeof import(\"@cometchat/calls-sdk-javascript\").CometChatCalls | null = null;\n     (async () => {\n       const mod = await import(\"@cometchat/calls-sdk-javascript\");\n       CallsSdk = mod.CometChatCalls;\n       // ... use CallsSdk\n     })();\n     return () => {\n       try { CallsSdk?.leaveSession(); } catch { /* noop */ }\n     };\n   }, []);\n   ```\n\nSkipping any of these reproduces the failure mode: the route 500s with either `Module not found: Can't resolve 'fs'` or `ReferenceError: window is not defined`. Verified empirically against Next.js 16.2.6 + Calls SDK 5.0.0-beta.2.\n\nFor Vite / React Router / Astro, none of this applies — those bundlers don't pre-evaluate client modules.\n\n---\n\n## Purpose\n\nProduction-grade voice + video calling for React-family web apps. Loaded by `cometchat-calls` when `framework` is one of `reactjs`, `nextjs`, `react-router`, or `astro`. Operates in two modes:\n\n- **Standalone** — calls is the product. `@cometchat/chat-sdk-javascript` (signaling) + `@cometchat/calls-sdk-javascript` (WebRTC) + a small set of UI Kit call components. No `CometChatConversations` / `CometChatMessageList` / etc.\n- **Additive** — calls layered onto an existing CometChat React UI Kit integration. Adds call buttons inline, mounts `CometChatIncomingCall` at app root.\n\n**Read these other skills first:**\n- `cometchat-calls` — dispatcher (modes, hard rules, anti-patterns)\n- `cometchat-core` — Chat SDK init, login, env-var prefix per framework, SSR safety\n- Framework-specific patterns: `cometchat-react-patterns` / `cometchat-nextjs-patterns` / `cometchat-react-router-patterns` / `cometchat-astro-patterns`\n\n**Ground truth:**\n- SDK source — `~/Downloads/calls-sdk/calls-sdk-javascript-5/package/`\n- Sample apps — `~/Downloads/calls-sdk/calls-sdk-javascript-5/sample-apps/{react,vue,angular,svelte,ionic}/`\n- Public docs — https://www.cometchat.com/docs/calls/javascript/overview\n\n---\n\n## 1. The seven hard rules — web specialization\n\n### 1.1 Dual-SDK contract\n\n`@cometchat/chat-sdk-javascript` for ringing; `@cometchat/calls-sdk-javascript` for the WebRTC session. They are separate npm packages.\n\n```ts\n// ✓ RIGHT — initiate ringing (Chat SDK)\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\n\nconst outgoing = new CometChat.Call(receiverUid, CometChat.CALL_TYPE.VIDEO, CometChat.RECEIVER_TYPE.USER);\nconst initiated = await CometChat.initiateCall(outgoing);\n// initiated.getSessionId() — the ID the Calls SDK will join\n```\n\n```ts\n// ✓ RIGHT — join WebRTC session (Calls SDK v5)\nimport { CometChatCalls } from \"@cometchat/calls-sdk-javascript\";\n\n// v5 — plain SessionSettings object, no Builder\nconst sessionSettings = {\n  sessionType: \"VIDEO\",   // or \"VOICE\"\n  layout: \"TILE\",\n};\n\n// v5 — generateToken takes ONLY sessionId (Calls SDK has its own auth state\n// after CometChatCalls.login(); no authToken arg needed).\nconst tokenRes = await CometChatCalls.generateToken(sessionId);\n\n// htmlElement is REQUIRED — pass the DOM container the SDK should draw into\nconst container = document.getElementById(\"ongoing-call-root\")!;\nconst result = await CometChatCalls.joinSession(tokenRes.token, sessionSettings, container);\nif (result?.error) {\n  console.error(\"joinSession failed:\", result.error);\n}\n```\n\nThe two-`Call`-classes problem from Android does NOT exist on JS — there's only one `CometChat.Call` constructor. But the dual-SDK split still trips up agents trained on the chat-only docs.\n\n### 1.2 VoIP push — N/A on web (browsers don't have VoIP push)\n\nThe mandatory-VoIP-push rule from mobile families does not apply to web. Browsers cannot ring a closed tab. The standalone-mode equivalent is **Web Push notifications** (Service Worker + `Notification` API + push subscriptions) — useful for nudging the user to a tab where the call screen is open, but they do not bypass tab/page-load.\n\nThe skill scaffolds Web Push as an opt-in (asks user); it is not strictly required. Production calls UX on web typically pairs with email/SMS fallback for missed calls, not VoIP.\n\n### 1.3 Lifecycle — `getUserMedia` cleanup\n\nWeb's equivalent of Android's foreground-service correctness is `MediaStream` track cleanup. Browsers don't release the camera/mic until tracks are explicitly stopped. The kit handles this for `<CometChatOngoingCall />`, but custom WebRTC surfaces (Section 4) must do:\n\n```ts\nfunction endCall() {\n  // 1. End the Calls SDK session — releases the kit's internal stream\n  CometChatCalls.leaveSession();   // v5 — was endSession() in v4 (still works as a deprecated shim)\n\n  // 2. If you grabbed a custom MediaStream (preview, screen-share), stop tracks\n  customStream?.getTracks().forEach(t => t.stop());\n  customStream = null;\n\n  // 3. Detach video elements\n  if (videoEl.current) videoEl.current.srcObject = null;\n}\n```\n\nSkipping this leaves the camera light on until the tab is closed. Same canonical bug as iOS rule 1.5.\n\n### 1.4 Server-minted auth tokens for production\n\nIn v5 the Calls SDK has **its own login step** — it no longer piggybacks on the Chat SDK's auth context implicitly. After `CometChat.login()` resolves on the chat side, call **`CometChatCalls.login(uid, apiKey)`** for dev or **`CometChatCalls.loginWithAuthToken(authToken)`** for production. The auth token is the same token your backend mints via the CometChat Create-Auth-Token API; the Calls SDK and Chat SDK accept it interchangeably.\n\n```ts\n// Dev\nawait CometChatCalls.login(uid, import.meta.env.VITE_COMETCHAT_API_KEY);\n\n// Production (server-minted token)\nawait CometChatCalls.loginWithAuthToken(authTokenFromBackend);\n```\n\n`cometchat-production` (web) covers the token-endpoint pattern.\n\n### 1.5 Hangup cleanup — see rule 1.3\n\n### 1.6 Permissions — `getUserMedia` prompts\n\nThe browser handles the runtime permission prompt automatically when the Calls SDK calls `getUserMedia`. The integration must:\n\n- Surface a `try/catch` around `startSession` to handle `NotAllowedError` (user denied)\n- Surface `NotFoundError` (no camera/mic on device — common on desktops with no webcam)\n- Render a clear in-app explanation BEFORE the browser prompts, so users know what they're agreeing to (browsers ignore this in autoplay/iframe contexts but it improves grant rates)\n\nThere are no manifest-level permission declarations on web. HTTPS is required — the skill detects `localhost` (allowed) vs other origins (must be HTTPS) and warns if the dev server is HTTP.\n\n### 1.7 IncomingCall mounted at app root\n\n`<CometChatIncomingCall />` (additive mode) or a Service-Worker-driven web-push handler (standalone mode) must mount above the route boundary so calls fire on every page.\n\n```tsx\n// app/layout.tsx (Next.js App Router) or App.tsx (Vite/CRA)\n<CometChatProvider>\n  <CometChatIncomingCall />   {/* renders nothing when no call active; listens app-wide */}\n  <Routes>...</Routes>\n</CometChatProvider>\n```\n\nMounting it inside a route component means it disappears on navigation — calls only ring on the screen where it's mounted. That's the canonical \"calls don't work\" bug on web.\n\n---\n\n## 2. Setup\n\n### Install\n\nCalls SDK v5.0.0 stable shipped but the npm `latest` dist-tag still points at v4.2.6 (legacy). **Always pin to `@5` (or a specific `^5.0.0` version)** — `npm install @cometchat/calls-sdk-javascript` (no tag) resolves to v4.2.6, which is the previous generation. `@beta` is also published but pins to an older 5.0.0-beta.2 — prefer `@5` to get the latest stable 5.x.\n\n```bash\n# v5 stable (current — pulls 5.0.0 or newer 5.x)\nnpm install @cometchat/chat-sdk-javascript @cometchat/calls-sdk-javascript@5\n# additive mode: @cometchat/chat-uikit-react is already installed\n```\n\nThe kit (`@cometchat/chat-uikit-react@^6.x`) was built against the v4 calls API but Calls SDK v5 ships v4 deprecated-method shims that delegate to v5 implementations — so the kit's `CometChatCallButtons` / `CometChatIncomingCall` / `CometChatOngoingCall` keep working when you swap v4 for v5. Custom call surfaces should use v5 APIs directly.\n\n### Init order (web — v5)\n\n```tsx\n// cometchat/init.ts\nimport { CometChat } from \"@cometchat/chat-sdk-javascript\";\nimport { CometChatCalls } from \"@cometchat/calls-sdk-javascript\";\n\nlet initialized = false;\n\nexport async function initCometChat() {\n  if (initialized) return;\n\n  // 1. Chat SDK init (signaling)\n  const appSettings = new CometChat.AppSettingsBuilder()\n    .subscribePresenceForAllUsers()\n    .setRegion(import.meta.env.VITE_COMETCHAT_REGION)  // adjust for framework\n    .build();\n  await CometChat.init(import.meta.env.VITE_COMETCHAT_APP_ID, appSettings);\n\n  // 2. Calls SDK init (WebRTC) — v5 takes a plain object and returns {success, error}\n  const callsInit = await CometChatCalls.init({\n    appId: import.meta.env.VITE_COMETCHAT_APP_ID,\n    region: import.meta.env.VITE_COMETCHAT_REGION,\n  });\n  if (!callsInit?.success) {\n    throw new Error(`CometChatCalls.init failed: ${JSON.stringify(callsInit?.error)}`);\n  }\n\n  initialized = true;\n}\n\n// After your existing CometChat.login(uid, apiKey) call, login the Calls SDK too.\n// In v5 the Calls SDK has its own auth state; this step is mandatory.\nexport async function loginCometChat(uid: string) {\n  await CometChat.login(uid, import.meta.env.VITE_COMETCHAT_AUTH_KEY);\n\n  // v5 — Calls SDK login. Either form is fine; loginWithAuthToken is for production.\n  if (!CometChatCalls.getLoggedInUser()) {\n    await CometChatCalls.login(uid, import.meta.env.VITE_COMETCHAT_API_KEY);\n    // OR: await CometChatCalls.loginWithAuthToken(serverMintedToken);\n  }\n}\n```\n\nThe module-level `initialized` flag prevents StrictMode double-init in React 18+ dev mode. The `getLoggedInUser()` guard prevents re-login on hot reload.\n\n### Framework-specific env prefixes (already covered by `cometchat-core`)\n\n| Framework | Env prefix |\n|---|---|\n| Vite (reactjs / react-router) | `VITE_` |\n| CRA | `REACT_APP_` |\n| Next.js | `NEXT_PUBLIC_` |\n| Astro | `PUBLIC_` |\n\n### SSR safety\n\nCometChat Calls SDK is browser-only — `window`, `MediaStream`, `RTCPeerConnection`, `navigator.mediaDevices`. Calls components must NOT render server-side:\n\n- **Next.js App Router:** add `\"use client\"` to the file containing call components\n- **Next.js Pages Router:** dynamic-import with `ssr: false`\n- **React Router:** lazy + `<Suspense>` + `if (typeof window === \"undefined\") return null` guard in the component\n- **Astro:** `client:only=\"react\"` on the call component island\n\n---\n\n## 3. Components catalog\n\n### Calls SDK primitives — v5 (used in standalone or wherever you build custom UI)\n\n| Class / function | Purpose |\n|---|---|\n| `CometChatCalls.init({ appId, region })` | One-time init. Returns `Promise<{ success, error }>` — check `.success`. |\n| `CometChatCalls.login(uid, apiKey)` | Dev-mode login. Returns the logged-in `User`. |\n| `CometChatCalls.loginWithAuthToken(authToken)` | Production login with server-minted token. |\n| `CometChatCalls.getLoggedInUser()` | Returns a plain `{ uid, name, avatar?, status?, ... }` object or `null`. Access `.uid` as a PROPERTY (not `.getUid()` — that method belongs to the Chat SDK's `User` class, which session-only code does not import). Use to guard against double-login: `if (existing && existing.uid === uid) return existing;` |\n| `CometChatCalls.logout()` | Clears Calls SDK auth state. |\n| `CometChatCalls.generateToken(sessionId)` | Mint a session-scoped RTC token. **Single arg** — auth is implicit after `login()`. |\n| `CometChatCalls.joinSession(callToken, sessionSettings, htmlElement)` | Join the WebRTC session — `htmlElement` is required. Returns `{ data, error }`. |\n| `CometChatCalls.leaveSession()` | End + cleanup. Returns `void`. |\n| `CometChatCalls.addEventListener(eventName, handler)` | Granular event subscription — replaces v4's monolithic `OngoingCallListener`. Returns an unsubscribe function. |\n| `CometChatCalls.setLayout(layout)` | `\"TILE\"` / `\"SIDEBAR\"` / `\"SPOTLIGHT\"`. Per-participant. |\n| `CometChatCalls.constants.LAYOUT` | Layout enum for type-safety. |\n\n**v4 → v5 method mapping** (the deprecated v4 method names below all still work in v5 as shims that delegate to v5 implementations — your kit's v6 code is unaffected):\n\n| v4 (deprecated) | v5 |\n|---|---|\n| `init(new CallAppSettingsBuilder().setAppId(...).build())` | `init({ appId, region })` |\n| `generateToken(sid, authToken)` | `generateToken(sid)` (after `login()`) |\n| `startSession(token, settings, el)` | `joinSession(token, settings, el)` |\n| `endSession()` | `leaveSession()` |\n| `setMode(mode)` | `setLayout(layout)` |\n| `OngoingCallListener` (single object) | `addEventListener(name, handler)` (granular) |\n| `enterPIPMode()` | `enablePictureInPictureLayout()` |\n\nSee `references/migration-v4-to-v5.md` for the full migration guide.\n\n### UI Kit components (additive mode — `@cometchat/chat-uikit-react`)\n\n| Component | Purpose |\n|---|---|\n| `<CometChatCallButtons user={u} />` | Voice + video icon row, drop into any header |\n| `<CometChatIncomingCall />` | Root-mounted; renders nothing when no call active |\n| `<CometChatOutgoingCall />` | Auto-mounted by IncomingCall on initiateCall |\n| `<CometChatOngoingCall />` | Active call view; hosts the WebRTC element |\n| `<CometChatCallLogs onItemClick={fn} />` | Paginated history |\n\nIn standalone mode, you can compose just `<CometChatOngoingCall />` + `<CometChatCallLogs />` from the UI Kit even without using `CometChatConversations` etc. — the kit's calls components don't depend on its conversation components.\n\n---\n\n## 4. Standalone integration\n\nWhen `product === \"voice-video\"` and there is no existing chat UI integration.\n\n**Split by calling mode — these are two different shapes:**\n\n### 4a. Standalone — Session mode (meeting-room UX, no ringing)\n\nCalls SDK ONLY. NO Chat SDK. Matches the upstream sample at `~/Downloads/calls-sdk/calls-sdk-javascript-5/sample-apps/cometchat-calls-sample-app-react/`. The skill scaffolds:\n\n1. **`cometchat/init.ts`** — `CometChatCalls.init({ appId, region, authKey })` ONLY. No `CometChat.init`, no `CometChat.login`. Pass `authKey` at init time so subsequent `CometChatCalls.login(uid)` calls need no second arg.\n2. **`cometchat/CometChatProvider.tsx`** — Runs Calls SDK init on mount, exposes `loggedInUser` via `CometChatCalls.getLoggedInUser()`, gates children on success.\n3. **`pages/Home.tsx`** — UID picker (dev mode) + \"Start meeting\" (mints UUID, navigates to `/meet/:id`) + \"Join meeting\" (paste sessionId).\n4. **`pages/CallRoom.tsx`** — `/meet/:sessionId` route. Container is `position: fixed; width: 100vw; height: 100vh`. `CometChatCalls.joinSession(token, {}, container)` with empty settings. See `references/call-session.md` for the canonical pattern.\n5. **HTTPS check** — warns if dev server is HTTP non-localhost.\n\n**Why no Chat SDK:** session mode never touches the Chat SDK call entity. Initializing both SDKs adds two failure modes (Chat init, Chat login race) for zero benefit. The upstream sample confirms this — it never imports `@cometchat/chat-sdk-javascript`.\n\n### 4b. Standalone — Ringing mode (CallButtons + Incoming/Outgoing/Ongoing kit components)\n\nDual-SDK: Chat SDK signaling channel + Calls SDK media channel. The skill scaffolds:\n\n1. **`cometchat/init.ts`** — Chat SDK + Calls SDK init (sequential), module-level guard.\n2. **`cometchat/CometChatProvider.tsx`** — React provider, runs init+login on mount, gates children on success.\n3. **`components/CallButton.tsx`** — Voice + video buttons next to a user (your existing user listing / profile page).\n4. **`/calls` route or screen** — Renders `<CometChatCallLogs />` for history. (Path depends on framework — `app/calls/page.tsx` for Next.js App Router, `routes/calls.tsx` for React Router, etc.)\n5. **`OngoingCallView.tsx`** — Custom WebRTC view OR delegates to `<CometChatOngoingCall />`. Implements rule 1.3 cleanup.\n6. **Provider mounts `<CometChatIncomingCall />`** at the layout root (rule 1.7).\n7. **Optional Web Push** — Service Worker registration + push subscription endpoint, if the user opts in.\n8. **HTTPS check** — warns if dev server is HTTP non-localhost.\n\n## 5. Additive integration\n\nWhen `cometchat-core` integration already exists. The skill:\n\n1. Adds `@cometchat/calls-sdk-javascript@5` to `package.json` (v5.0.0 stable — see Install section).\n2. Patches `cometchat/init.ts` to call `CometChatCalls.init({...})` after `CometChat.init` AND to call `CometChatCalls.login(uid, apiKey)` after `CometChat.login` (v5 — separate auth).\n3. Mounts `<CometChatIncomingCall />` at the layout root next to existing components (rule 1.7).\n4. Adds `<CometChatCallButtons user={user} />` inline on selected screens — usually inside `<CometChatMessageHeader />` (the kit auto-renders it there if a `user` prop is set).\n5. Adds a `/calls` route for `<CometChatCallLogs />` if the user picked the \"dedicated route\" option.\n\n## 6. Anti-patterns\n\n1. **Mounting `<CometChatIncomingCall />` inside a route component.** Disappears on navigation. Mount above the route boundary in the layout (rule 1.7).\n2. **Initializing both SDKs in parallel.** `CometChatCalls.init` requires the Chat SDK's app-id context; calling them with `Promise.all` causes intermittent \"auth context null\" errors. Sequence: chat init → calls init.\n3. **Skipping the `initialized` guard.** React StrictMode renders effects twice in dev — without the guard, you get duplicate listeners and double-init warnings.\n4. **Forgetting `getTracks().forEach(t => t.stop())` on custom streams.** Camera light stays on until tab close. Rule 1.3.\n5. **Embedding `<CometChatOngoingCall />` in an `<iframe>` without `allow=\"camera; microphone\"`.** Browsers silently deny `getUserMedia`. The skill detects iframe contexts and writes the allow list.\n6. **Calling `joinSession` (v5) before `CometChatCalls.login()` resolves.** `generateToken` 401s — no Calls SDK auth state. Sequence: `CometChatCalls.init` → `CometChatCalls.login` → `generateToken` → `joinSession`.\n7. **Installing `@cometchat/calls-sdk-javascript` without a version pin.** Resolves to v4.2.6 (the `latest` dist-tag) instead of v5.0.0. The kit's v4 deprecated-method shims live INSIDE v5 — picking up plain v4 means you don't get them. Always `@5` (recommended) or pin a specific `^5.0.0` version. `@beta` is also valid but pins to an older `5.0.0-beta.2`.\n7. **Running over HTTP (non-localhost).** `getUserMedia` returns `NotAllowedError`. The skill warns; user must use HTTPS or `localhost`.\n\n## 7. Verification checklist\n\n**Static:**\n\n- [ ] Both `@cometchat/chat-sdk-javascript` and `@cometchat/calls-sdk-javascript@5` (v5 stable) in `package.json`\n- [ ] `CometChatCalls.login(uid, apiKey)` is called after `CometChat.login` (v5 separate auth)\n- [ ] Init order: chat init → calls init (sequential, not parallel)\n- [ ] `<CometChatIncomingCall />` mounted at layout root (additive mode)\n- [ ] Cleanup path stops MediaStream tracks + ends Calls SDK session\n- [ ] Framework-correct SSR guard (`\"use client\"` / `ssr: false` / `client:only`)\n- [ ] Env vars use the framework-correct prefix\n- [ ] Module-level `initialized` flag for StrictMode safety\n\n**Runtime (browser):**\n\n- [ ] Outgoing call connects, two-way audio + video\n- [ ] Incoming call rings on a separate page within the same SPA\n- [ ] Camera light off within 2 seconds of hangup\n- [ ] Tab refresh during call cleanly disconnects (no orphaned session)\n- [ ] HTTPS or localhost only — `getUserMedia` works\n- [ ] Multi-tab: closing one tab doesn't end the call in another tab\n\n## 8. Pointers\n\n- `cometchat-core` — provider pattern, init guard, login order\n- `cometchat-components` — full UI Kit catalog (additive mode)\n- `cometchat-{nextjs,react,react-router,astro}-patterns` — framework-specific SSR guards, route placement\n- `cometchat-production` — server-minted tokens, security\n- `cometchat-troubleshooting` — common web failure modes (HTTPS, iframe permissions, StrictMode double-init)","tags":["cometchat","react","calls","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react-native"],"capabilities":["skill","source-cometchat","skill-cometchat-react-calls","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-react-calls","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 (28,721 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:55.751Z","embedding":null,"createdAt":"2026-05-18T07:04:29.530Z","updatedAt":"2026-05-18T19:04:55.751Z","lastSeenAt":"2026-05-18T19:04:55.751Z","tsv":"'-05':1056 '-1':531,551 '-12':1057 '/_components/cometchatgate':948 '/calls':3044,3194 '/cometchat/cometchatprovider':833 '/docs/calls/javascript/overview':1385 '/downloads/calls-sdk/calls-sdk-javascript-5/package':1372 '/downloads/calls-sdk/calls-sdk-javascript-5/sample-apps':1375 '/downloads/calls-sdk/calls-sdk-javascript-5/sample-apps/cometchat-calls-sample-app-react':2844 '/meet':96,971,1016,1023,1038,2901,2909 '/skill.md':150 '0':438,464,484,506,522,587 '1':121,216,247,277,278,316,728,1386,1702,2225,2848,3003,3125,3209 '1.1':1393 '1.2':1558 '1.3':1657,1880,3075,3300 '1.4':1773 '1.5':1772,1875 '1.6':1881 '1.7':1986,3085,3166,3227 '100':472,481,581 '100px':585 '100vh':584,2919 '100vw':2917 '16':745 '16.2.6':1219 '18':2367 '2':139,220,274,320,759,1726,2068,2250,2873,3015,3136,3228,3519 '2026':1055 '3':174,794,1050,1085,1746,2472,2889,3028,3155,3259 '3.0':136,152 '4':1058,1696,2798,2907,3043,3167,3283 '400':557 '401s':3331 '4a':2823 '4b':2981 '5':1138,2091,2122,2128,2138,2144,2932,3065,3113,3128,3191,3301,3382,3428 '5.0.0':1222,2095,2119,2135,3388,3399 '500s':1199 '6':2154,3077,3205,3323 '7':3086,3342,3401,3420 '8':3101,3552 'accept':296,1011,1845 'accepted.getsessionid':1018 'access':2537 'across':347 'activ':2031,2750,2758 'add':760,1308,2432,2960,3126,3168,3192 'addeventlisten':2710 'addit':1297,1992,2145,2726,3114,3456,3570 'adjust':2239 'agent':309,1550 'agre':1941 'alias':740 'allow':1971,3306,3321 'alon':687 'alreadi':2149,2385,3121 'also':2112,3392 'alway':2088,3381 'ambigu':177 'ancestor':605 'android':1052,1529,1665 'angular':1378 'anoth':286,3550 'anti':1330,3207 'anti-pattern':1329,3206 'api':1602,1838,1855,2162,2199,2348 'apikey':1813,2295,2506,3149,3435 'app':12,719,856,1152,1254,1315,1374,1929,1990,2021,2034,2247,2271,2402,2430,3058,3241 'app-id':3240 'app-wid':2033 'app.tsx':2024 'app/_components/cometchatgate.tsx':817 'app/calls/page.tsx':3055 'app/layout.tsx':891,940,2019 'app/meet/layout.tsx':901 'appid':2268,2492,2684,2851 'appli':638,724,1232,1581 'appset':2231,2249 'arg':1483,2591,2872 'arm':907 'around':1905 'ask':187,1635 'astro':18,1228,1271,1366,2406,2463,3578 'async':1007,1118,1173,2219,2317 'audio':3502 'auth':1477,1777,1800,1822,1836,2310,2327,2579,2592,3154,3250,3335,3442 'authkey':2853,2860 'authtoken':1482,1818,2518,2688 'authtokenfrombackend':1864 'auto':325,479,483,614,2752,3181 'auto-mod':324 'auto-mount':2751 'auto-rend':3180 'automat':1892 'autoplay/iframe':1947 'avatar':2532 'await':997,1012,1073,1124,1130,1176,1430,1487,1511,1850,1862,2243,2266,2322,2343,2351 'backend':1829 'bash':2130 'bear':986 'belong':2546 'belt':1099 'belt-and-brac':1098 'benefit':2971 'beta':2110,3390 'beta.2':1223,2120,3400 'bias':331 'boundari':2011,3222 'brace':1101 'break':651 'brows':912 'browser':716,1564,1584,1675,1886,1933,1943,2415,3309,3495 'browser-on':2414 'bug':454,1768,2065 'build':231,752,754,2242,2485,2682 'builder':1458 'built':668,2157 'built-in':667 'bundler':644,679,1234 'button':1310,3032 'bypass':1623 'calc':583 'call':4,6,28,93,132,149,183,227,233,281,285,303,401,413,442,597,617,640,874,919,1008,1020,1027,1032,1220,1248,1259,1277,1291,1298,1309,1324,1437,1446,1472,1507,1525,1615,1643,1654,1705,1784,1810,1840,1895,1897,2013,2030,2047,2061,2071,2161,2164,2194,2251,2296,2299,2305,2330,2411,2421,2439,2469,2475,2577,2749,2759,2789,2816,2833,2868,2876,2955,2996,3007,3140,3146,3244,3257,3324,3333,3437,3447,3464,3497,3505,3526,3548 'call.getsessionid':1014,1025 'callappsettingsbuild':2680 'callbutton':2985 'caller':935 'calllisten':871 'callsinit':2265,2278,2286 'callsmodul':1112,1128,1129 'callsmodule.cometchatcalls':1137 'callssdk':1166,1179,1182,1185 'calltoken':2598 'camera':1758,3292,3307,3515 'camera/mic':1680,1915 'cannot':1585 'canon':1767,2060,2930 'catalog':2474,3569 'catch':1187 'caus':3248 'chain':501 'channel':2995,2999 'chat':26,447,635,1335,1415,1555,1797,1808,1843,2226,2549,2811,2837,2946,2953,2964,2966,2992,3005,3237,3255,3445 'chat-layout':446,634 'chat-on':1554 'chatmodul':1105,1122,1123 'chatmodule.cometchat':1135 'check':122,676,2502,2934,3103 'checklist':3422 'child':461 'children':844,845,848,953,954,957,2886,3025 'claim':533 'class':1526,2488,2553 'classifi':327 'classnam':490,549 'clean':3527 'cleanup':1660,1674,1877,2613,3076,3458 'clear':1926,2576 'client':686,694,805,819,1240,2434,2464,3473,3476 'close':1588,1765,3298,3541 'code':43,49,366,649,2558,2672 'column':517,604 'cometchat':2,5,131,148,348,630,996,1134,1258,1303,1323,1333,1352,1356,1360,1365,1418,1833,1854,1866,2208,2237,2246,2270,2275,2326,2347,2389,2410,3118,3555,3564,3572,3588,3596 'cometchat-astro-pattern':1364 'cometchat-cal':130,147,1257,1322 'cometchat-compon':3563 'cometchat-cor':1332,2388,3117,3554 'cometchat-nextjs-pattern':1355 'cometchat-product':1865,3587 'cometchat-react-cal':1 'cometchat-react-pattern':629,1351 'cometchat-react-router-pattern':1359 'cometchat-troubleshoot':3595 'cometchat.acceptcall':1013 'cometchat.addcalllistener':1000 'cometchat.appsettingsbuilder':2233 'cometchat.call':1424,1539 'cometchat.call_type.video':1426 'cometchat.calllistener':1005 'cometchat.init':2244,2856,3143 'cometchat.initiatecall':1431 'cometchat.login':1804,2293,2323,2858,3151,3439 'cometchat.receiver_type.user':1427 'cometchat/calls-sdk-javascript':20,663,1115,1132,1169,1178,1283,1401,1452,2099,2143,2214,3127,3344,3427 'cometchat/chat-sdk-javascript':656,999,1108,1126,1281,1398,1420,2142,2210,2980,3425 'cometchat/chat-uikit-react':2147,2153,2728 'cometchat/cometchatprovider.tsx':2874,3016 'cometchat/config.json':123 'cometchat/init.ts':2206,2849,3004,3138 'cometchatcal':1095,1136,1170,1450,2212 'cometchatcallbutton':80,363,2182,2731,3169 'cometchatcalllog':2765 'cometchatcalls.addeventlistener':2616 'cometchatcalls.constants.layout':2639 'cometchatcalls.generatetoken':1488,2581 'cometchatcalls.getloggedinuser':2342,2526,2884 'cometchatcalls.init':2267,2283,2491,2850,3141,3234,3338 'cometchatcalls.joinsession':101,1512,2597,2920 'cometchatcalls.leavesession':1714,2611 'cometchatcalls.login':1480,1811,1851,2344,2504,2866,3147,3328,3339,3433 'cometchatcalls.loginwithauthtoken':1817,1863,2352,2517 'cometchatcalls.logout':2575 'cometchatcalls.setlayout':2631 'cometchatconvers':1294,2784 'cometchatg':843,946 'cometchatincomingcal':34,77,364,1313,2183 'cometchatmessagelist':1295 'cometchatongoingcal':36,365,2184 'cometchatoutgoingcal':35 'cometchatprovid':797,830,867,991 'common':453,1918,3598 'compon':373,695,814,942,1292,2041,2422,2440,2462,2470,2473,2725,2729,2790,2797,2988,3164,3214,3565 'componen':37 'components/callbutton.tsx':3029 'compos':2775 'confer':251 'config':645,777,790 'config.resolve':781,782 'config.resolve.fallback':783,784 'confirm':155,369,2975 'connect':3498 'consist':351 'console.error':1519 'const':773,829,995,1010,1174,1421,1428,1459,1485,1502,1509,2230,2264 'constructor':1540 'contact':84 'contain':104,402,417,424,528,572,593,598,1496,1503,1515,2438,2912,2922 'containerref':489,548,578 'content':607 'context':1801,1948,3243,3251,3317 'contract':1397 'convers':2796 'core':1334,2390,3119,3556 'correct':1670,3469,3484 'cover':19,1869,2386 'cra':14,2400 'crash':486 'creat':802,1835 'create-auth-token':1834 'current':2133 'custom':1692,1731,2193,2486,3067,3290 'customstream':1739,1744 'dashboard':340 'data':2609 'decid':57 'declar':1961 'declin':298 'dedic':3202 'default':192,609,792,950 'defer':706 'defin':1214 'deleg':2174,2664,3071 'deni':1911,3311 'depend':2793,3052 'deprec':1724,2170,2651,2676,3365 'deprecated-method':2169,3364 'descript':206,253,282 'desktop':1920 'detach':1747 'detect':1969,3315 'dev':748,750,1815,1849,1982,2368,2508,2893,2937,3106,3270 'dev-mod':2507 'dev/build':730 'devic':293,1917 'differ':2821 'dimens':408,425 'direct':1096,2200 'disappear':2044,3215 'disconnect':3528 'dismiss':1034 'dispatch':133,1325 'display':238 'dist':2081,3355 'dist-tag':2080,3354 'div':487,546,576 'doc':1382,1557 'document.getelementbyid':1504 'doesn':3544 'dom':1495 'doubl':2363,2567,3280,3607 'double-init':2362,3279,3606 'double-login':2566 'draw':1500 'driven':1999 'drop':2738 'dual':23,1395,1544,2990 'dual-sdk':22,1394,1543,2989 'duplic':3276 'dynam':821,831,1159,2445 'dynamic-import':1158,2444 'e.g':178,564 'effect':3267 'either':1201,2333 'el':2696,2700 'element':1749,2764 'email/sms':1650 'embed':3302 'empir':1216 'empti':2924 'enablepictureinpicturelayout':2715 'encodeuricompon':1017,1024 'end':1045,1047,1703,2612,3463,3546 'end-to-end':1044 'endcal':1701 'endpoint':1873,3095 'endsess':1717,2701 'enter':109 'enterpipmod':2714 'entir':59 'entiti':2956 'enum':2641 'env':1340,2383,2392,3478 'env-var':1339 'equival':100,443,1594,1663 'error':1518,2263,2282,2287,2501,2610,3253 'etc':1296,2785,3064 'evalu':693,1239 'even':391,2781 'event':2620 'eventnam':2617 'everi':2016 'exact':207,241 'execut':713 'exist':1302,1532,2292,2570,2574,2810,3038,3122,3163 'existing.uid':2571 'experi':228 'explan':1930 'explicit':509,538,573,1684 'export':791,841,949,2218,2316 'expos':2881 'facetim':300 'fail':1521,2284 'failur':1195,2962,3600 'fallback':510,764,1651 'fals':236,786,788,801,838,2217,2449,3475 'famili':1252,1578 'file':2437 'fine':980,2336 'fire':411,2014 'first':244,337,357,1321 'fix':497,566,690,2915 'fixed-height':565 'flag':2359,3490 'flex':450,460,500,516,530,550,563,592,603 'flex-column':602 'flex-shrink':449 'flow':400 'fn':2767 'foreach':1741,3286 'foreground':1668 'foreground-servic':1667 'forget':3284 'form':2334 'found':1204 'fragil':742 'framework':1261,1344,1348,2241,2381,2391,3054,3468,3483,3581 'framework-correct':3467,3482 'framework-specif':1347,2380,3580 'fs':670,738,763,785,1208 'full':457,469,493,496,554,2720,3566 'function':842,951,1078,1119,1700,2220,2318,2489,2630 'gate':672,1082,2885,3024 'generat':2109 'generatetoken':1468,2686,2689,3330,3340 'get':619,2124,3275,3379 'getloggedinus':2371 'getrtctoken':30 'gettrack':1740,3285 'getuid':2543 'getusermedia':1659,1883,1898,3312,3408,3536 'give':570 'global':870 'googl':267 'got':159 'grab':1729 'grade':1245 'grant':1952 'granular':2619,2713 'ground':1368 'group':280,290 'guard':2372,2459,2564,3014,3263,3273,3471,3560,3584 'guid':2722 'h':456,468,492,505,521 'h-full':455,467,491 'handl':1041,1688,1887,1908 'handler':1157,2003,2618,2712 'hangup':1876,3522 'hard':1327,1389 'header':86,232,2741 'height':436,471,477,567,582,613,2918 'histori':2769,3050 'home':927 'host':2761 'hot':2378 'hous':595 'htmlelement':1490,2600,2605 'http':1985,2940,3109,3404 'https':1964,1977,2933,3102,3417,3532,3602 'huddl':273 'hydrat':702 'icon':2736 'id':113,261,972,1435,2248,2272,2902,3242 'ifram':3316,3603 'ignor':1944 'implement':378,2177,2667,3073 'implicit':1802,2594 'import':665,709,768,820,824,832,945,998,1071,1074,1107,1114,1125,1131,1144,1160,1168,1177,1417,1449,2207,2211,2446,2561,2979 'import.meta.env.vite':1853,2236,2245,2269,2274,2325,2346 'improv':1951 'in':669 'in-app':1927 'incom':92,873,918,3504 'incoming/outgoing/ongoing':2986 'incomingcal':1987,2755 'infer':140,153 'init':25,1337,2201,2228,2253,2364,2497,2678,2683,2862,2878,2965,3009,3020,3256,3258,3281,3443,3446,3448,3559,3608 'init.ts':1065,1087 'init/login':1077 'initcometchat':2221 'initi':698,1413,1429,2216,2223,2288,2358,2957,3229,3262,3489 'initiatecal':2757 'initiated.getsessionid':1433 'inlin':1311,3172 'insid':990,1064,1075,1155,2038,3177,3211,3369 'instal':21,2070,2098,2141,2150,3134,3343 'instead':173,3357 'integr':8,60,182,386,1307,1900,2800,2813,3115,3120 'interchang':1847 'intermitt':3249 'intern':1712 'io':1770 'ionic':1380 'island':2471 'isn':982 'isserv':778,780 'join':256,1440,1443,2601,2903 'joinsess':410,420,1042,1520,2697,3325,3341 'js':1534 'json':746 'json.stringify':2285 'keep':2185 'key':1856,2328,2349 'kind':225 'kit':32,376,1290,1306,1687,1710,2152,2180,2669,2724,2780,2787,2987,3179,3361,3568 'know':1937 'label':203,248,275 'land':920 'latest':2079,2126,3353 'layer':1299 'layout':448,636,815,864,890,899,944,978,1465,2632,2640,2706,3082,3159,3225,3454 'lazi':1060,2452 'lazy-load':1059 'leav':1756 'leavesess':1186,2702 'legaci':2087 'let':323,1104,1111,1165,2215 'level':1069,1142,1959,2357,3013,3488 'lifecycl':1658 'light':1759,3293,3516 'like':266,299,900 'line':47,158 'link':263 'list':3040,3322 'listen':881,904,981,989,1003,2032,3277 'live':3368 'load':661,839,840,985,1061,1255 'load-bear':984 'loadsdk':1120 'localhost':1970,2943,3112,3407,3419,3534 'lock':384 'log':2514 'logged-in':2513 'loggedinus':2882 'login':993,1338,1789,2297,2332,2376,2510,2520,2568,2596,2692,2967,3021,3561 'logincometchat':2319 'loginwithauthtoken':2337 'longer':1793 'm':835 'm.cometchatprovider':836 'mandatori':39,643,1572,2315 'mandatory-voip-push':1571 'manifest':1958 'manifest-level':1957 'map':2649 'match':2839 'matter':352,588 'mean':902,2042,3375 'measur':415 'media':2998 'mediastream':1672,1732,2418,3461 'meet':170,250,268,398,2828,2896,2904 'meeting-room':169,397,2827 'messag':85 'method':2171,2545,2648,2653,3366 'microphon':3308 'migrat':2721 'min':504,520,612 'min-h':503,519 'min-height':611 'minheight':539,556,586 'mint':1776,1830,1860,2524,2583,2897,3592 'miss':1653 'mobil':1577 'mod':1175 'mod.cometchatcalls':1180 'modal':568 'mode':53,72,118,125,234,326,370,853,880,962,988,1196,1275,1326,1593,1993,2005,2146,2369,2509,2704,2727,2772,2817,2826,2894,2949,2963,2984,3457,3571,3601 'modul':660,1202,1241,2356,3012,3487 'module-level':2355,3011,3486 'monolith':2625 'mount':854,886,892,1312,1988,2007,2036,2056,2744,2753,2880,3023,3079,3156,3210,3218,3452 'multi':3539 'multi-tab':3538 'multipl':254 'multiselect':235 'must':51,313,403,430,884,1697,1901,1975,2006,2423,3415 'n/a':1561 'name':2531,2654,2711 'navig':69,967,2046,2899,3217 'navigator.mediadevices':2420 'near':81 'need':704,1484,2869 'net':541 'never':2950,2978 'new':1004,1423,2232,2281,2679 'newer':2137 'next':744,749,753,757,772,2404,3033,3161 'next.config.ts':766 'next.js':15,641,652,692,718,1218,2020,2403,2429,2441,3057 'next/dynamic':799,823 'nextconfig':770,774,775,793 'nextj':1266,1357,3573 'node':666 'non':406,2942,3111,3406 'non-localhost':2941,3110,3405 'non-zero':405 'none':1229 'noop':1188 'notallowederror':1909,3410 'notfounderror':1913 'noth':2027,2746 'notif':1598,1601 'npm':1409,2078,2097,2140 'nudg':1607 'null':1109,1110,1116,1117,1171,1172,1745,1753,2458,2536,3252 'number':427 'object':1456,2259,2534,2709 'older':2118,3398 'one':46,157,283,1263,1538,2495,3542 'one-tim':2494 'ongo':1506 'ongoing-call-root':1505 'ongoingcalllisten':2626,2707 'ongoingcallview.tsx':3066 'onincomingcallcancel':1031 'onincomingcallreceiv':1006 'onitemclick':2766 'onoutgoingcallaccept':1019 'onoutgoingcallreject':1026 'onto':1300 'open':1618 'oper':1272 'opt':1633,3099 'opt-in':1632 'option':214,215,219,237,315,319,358,3087,3204 'order':120,201,242,306,346,2202,3444,3562 'origin':1974 'orphan':3530 'outgo':1422,1432,3496 'overflow':608 'packag':1410 'package.json':734,3130,3432 'page':722,928,1040,1092,1147,2017,2442,3042,3510 'pages/callroom.tsx':2908 'pages/home.tsx':2890 'pagin':2768 'pair':1648 'parallel':3233,3451 'parent':475,591 'parti':108,966 'particip':2638 'pass':655,700,1493,2859 'past':2905 'patch':3137 'path':671,739,787,3051,3459 'pattern':632,1331,1350,1354,1358,1363,1367,1874,2931,3208,3558,3579 'peer':1053 'per':1343,2637 'per-particip':2636 'permiss':1882,1890,1960,3604 'pick':3200,3371 'picker':2892 'piggyback':1794 'pin':2089,2115,3348,3385,3395 'pixel':574,1049 'placement':850,3586 'plain':1454,2258,2529,3373 'plus':536 'point':2084 'pointer':3553 'posit':432,2914 'pre':1238 'pre-evalu':1237 'precondit':40 'prefer':359,2121 'prefix':1342,2384,2393,3485 'preserv':199 'prevent':2360,2373 'preview':1733 'previous':2108 'primit':312,2477 'problem':1527 'product':349,1244,1280,1642,1780,1820,1857,1867,2340,2519,2802,3589 'production-grad':1243 'profil':3041 'promis':2499 'promise.all':3247 'prompt':197,1884,1891,1934 'prop':3188 'properti':2541 'provid':849,1080,3018,3078,3557 'public':1381,2405,2407 'publish':2113 'pull':2134 'purpos':1242,2490,2730 'push':1560,1569,1574,1597,1603,1629,2002,3089,3093 'qualifi':186 'question':223 'race':2968 'rate':1953 're':924,1940,2375 're-login':2374 'reachabl':1150 'react':3,11,16,631,828,1226,1251,1268,1304,1353,1361,1376,2366,2397,2401,2450,2466,3017,3062,3264,3574,3576 'react-famili':1250 'react-rout':1267,2396,3575 'react.reactnode':955 'reactj':1265,2395 'reactnod':826,846 'read':1317 'receiveruid':1425 'recipi':87,291 'recommend':3383 'ref':488,547,577 'refer':75,657 'referenceerror':1210 'references/call-session.md':114,2927 'references/migration-v4-to-v5.md':2717 'references/ringing-integration.md':94 'refresh':3524 'region':2238,2273,2276,2493,2685,2852 'regist':868 'registr':3092 'releas':1678,1708 'reload':2379 'remain':534 'render':314,1924,2026,2425,2745,3048,3182,3266 'reorder':332 'rephras':210 'replac':1066,2622 'reproduc':1193 'requir':1492,1641,1966,2607,3235 'resolv':52,117,462,684,1207,1805,2102,3329,3349 'result':1510,1517 'result.error':1522 'return':789,847,956,1133,1183,2224,2261,2457,2498,2511,2527,2573,2608,2614,2627,3409 'right':512,1412,1442 'ring':54,76,90,106,163,194,222,245,265,276,294,371,380,389,852,879,964,987,1002,1400,1414,1586,2049,2832,2983,3506 'ringing-listen':1001 'ringing-shap':388 'room':171,252,399,2829 'root':79,857,889,943,1316,1508,1991,2743,3083,3160,3455 'root-mount':2742 'rootlayout':952 'rout':98,863,898,917,932,977,1154,1198,2010,2040,2911,3045,3195,3203,3213,3221,3585 'router':17,720,723,1153,1227,1269,1362,2022,2398,2431,2443,2451,3059,3063,3577 'router.push':1015,1022 'routes/calls.tsx':3060 'row':2737 'rtc':2588 'rtcpeerconnect':2419 'rule':307,637,1328,1390,1575,1771,1879,3074,3084,3165,3226,3299 'run':421,2875,3019,3402 'runtim':675,1889,3494 'safeti':540,1346,2409,2645,3493 'said':180 'sampl':1373,2842,2974 'say':164 'scaffold':63,189,1627,2847,3002 'scope':2587 'screen':89,1616,1735,2052,3047,3175 'screen-shar':1734 'script':735,747 'sdk':7,24,27,29,414,485,708,1143,1162,1221,1336,1370,1396,1416,1438,1447,1473,1498,1545,1706,1785,1798,1841,1844,1896,2072,2165,2227,2252,2300,2306,2331,2412,2476,2550,2578,2834,2838,2877,2947,2954,2991,2993,2997,3006,3008,3238,3334,3465 'sdks':647,1063,2959,3231 'second':246,2871,3520 'section':513,1695,3135 'secur':3594 'see':146,878,936,1878,2716,2926,3133 'select':3174 'separ':1408,3153,3441,3509 'sequenc':3254,3337 'sequenti':3010,3449 'server':813,941,1775,1859,1983,2427,2523,2938,3107,3591 'server-compon':812 'server-mint':1774,1858,2522,3590 'server-sid':2426 'servermintedtoken':2353 'servic':1599,1669,1997,3090 'service-worker-driven':1996 'session':55,95,112,218,243,249,259,334,960,1405,1445,1707,2556,2586,2604,2825,2948,3466,3531 'session-on':959,2555 'session-scop':2585 'sessionid':97,1039,1471,1489,2582,2906,2910 'sessionset':1455,1460,1514,2599 'sessiontyp':1461 'set':127,161,1287,2695,2699,2925,3190 'setappid':2681 'setlayout':2705 'setmod':2703 'setregion':2235 'setup':2069 'seven':1388 'shape':61,74,390,2822 'share':67,970,1089,1736 'shim':1725,2172,2662,3367 'ship':648,2075,2167 'short':544 'show':1029 'shown':336 'shrink':451,526 'sid':2687,2690 'side':1809,2428 'sidebar':2634 'signal':1282,2229,2994 'silent':383,921,3310 'singl':2590,2708 'skill':1320,1626,1968,2846,3001,3124,3314,3412 'skill-cometchat-react-calls' 'skip':1189,1754,3260 'slack':272 'small':289,804,1286 'sourc':1371 'source-cometchat' 'spa':3514 'space':535 'special':1392 'specif':589,1349,2094,2382,3387,3582 'split':1546,2814 'spotlight':2635 'squeez':620 'ssr':642,654,699,800,837,1345,2408,2448,3470,3474,3583 'stabl':2074,2127,2132,3132,3430 'standalon':1276,1592,2004,2481,2771,2799,2824,2982 'standalone-mod':1591 'standard':343 'start':756,758,2895 'startsess':1906,2693 'state':1478,2311,2580,3336 'static':683,1070,3423 'status':2533 'stay':3294 'step':135,151,1084,1790,2313 'still':176,680,1547,1720,2083,2657 'stop':38,1685,1737,3460 'stream':1713,3291 'strict':305,1640 'strict-ord':304 'strictmod':2361,3265,3492,3605 'string':2321 'style':555,579 'sub':862,897,916,976 'sub-rout':861,896,915,975 'subject':356 'subscribepresenceforallus':2234 'subscript':1604,2621,3094 'subsequ':2865 'success':2262,2279,2500,2503,2888,3027 'surfac':71,73,350,618,1694,1902,1912,2195 'svelt':1379 'swap':213,2189 'switch':729 'synchron':418 't.stop':1743,3288 'tab':1589,1612,1763,3297,3523,3540,3543,3551 'tab/page-load':1624 'tabl':154 'tag':2082,2101,3356 'take':1469,2256 'team':341 'throw':423,2280 'tile':429,1466,2633 'time':662,2496,2863 'timeout/rejection':938 'toast':1030 'token':1778,1823,1827,1837,1861,1872,2525,2589,2694,2698,2921,3593 'token-endpoint':1871 'tokenr':1486 'tokenres.token':1513 'top':1068,1141 'top-level':1067,1140 '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' 'touch':2951 'track':1673,1682,1738,3462 'train':1551 'trap':452,627 'tri':681,1184 'trip':1548 'troubleshoot':3597 'true':2289 'trust':137 'truth':1369 'try/catch':1904 'ts':767,994,1103,1411,1441,1699,1848 'tsx':465,511,575,816,939,1163,2018,2205 'turbopack':736 'twice':3268 'two':1274,1524,2820,2961,3500 'two-way':3499 'type':769,825,2644 'type-safeti':2643 'typeof':1106,1113,1167,2454 'typic':1647 'u':2733 'ui':68,311,1036,1289,1305,2487,2723,2779,2812,3567 'uid':1812,1852,2294,2320,2324,2345,2505,2530,2538,2572,2867,2891,3148,3434 'unaffect':2674 'undefin':2456 'unsubscrib':2629 'upstream':2841,2973 'url':172,973 'use':195,381,498,529,562,685,808,818,1094,1181,1605,2197,2433,2479,2562,2783,3416,3472,3480 'useeffect':1156,1164 'user':83,143,179,255,284,394,910,1609,1636,1910,1936,2516,2552,2732,3036,3039,3098,3170,3171,3187,3199,3414 'usual':3176 'uuid':2898 'ux':1644,2830 'v4':1719,2160,2168,2190,2623,2646,2652,2675,3363,3374 'v4.2.6':2086,2104,3351 'v5':664,1448,1453,1467,1715,1782,2131,2166,2176,2192,2198,2204,2255,2303,2329,2478,2647,2660,2666,2677,3152,3326,3370,3429,3440 'v5.0.0':2073,3131,3359 'v6':1051,2671 'valid':1043,3393 'var':1341,3479 'verbatim':198 'verif':3421 'verifi':1215 'version':2096,3347,3389 'via':1151,1831,2883 'video':1247,1462,1748,2735,2805,3031,3503 'videoel.current':1751 'videoel.current.srcobject':1752 'view':2760,3069 'viewport':545 'vite':13,1225,2394,2399 'vite/cra':2025 'voic':1246,1464,2734,2804,3030 'voice-video':2803 'void':2615 'voip':1559,1568,1573,1656 'vs':1972 'vue':1377 'w':495,553 'w-full':494,552 'want':168,395 'warn':1979,2935,3104,3282,3413 'way':3501 'web':10,1253,1391,1563,1583,1596,1628,1646,1661,1868,1963,2001,2067,2203,3088,3599 'web-push':2000 'webcam':1923 'webpack':732,751,755,762,776 'webrtc':1284,1404,1444,1693,2254,2603,2763,3068 'whatsapp':302 'wherev':2483 'whose':606 'wide':2035 'width':434,580,2916 'window':658,1211,2417,2455 'within':3511,3518 'without':367,599,2782,3271,3305,3345 'word':145 'work':1721,2064,2186,2658,3537 'worker':1600,1998,3091 'wrap':795 'wrapper':806 'write':45,362,3319 'wrong':466 'www.cometchat.com':1384 'www.cometchat.com/docs/calls/javascript/overview':1383 'x':2129,2139,2155 'zero':407,622,2970 'zoom':269","prices":[{"id":"254e6751-8154-4abe-8717-6758845c2f53","listingId":"da98c6c9-65cc-42bc-ad3f-92a5a0e850d1","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-18T07:04:29.530Z"}],"sources":[{"listingId":"da98c6c9-65cc-42bc-ad3f-92a5a0e850d1","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-react-calls","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-react-calls","isPrimary":false,"firstSeenAt":"2026-05-18T07:04:29.530Z","lastSeenAt":"2026-05-18T19:04:55.751Z"}],"details":{"listingId":"da98c6c9-65cc-42bc-ad3f-92a5a0e850d1","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-react-calls","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":"6c3c76596fab644520dd7b71b7cb969060450a39","skill_md_path":"skills/cometchat-react-calls/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-react-calls"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-react-calls","license":"MIT","description":"CometChat Calls SDK integration for web React apps (Vite, CRA, Next.js, React Router, Astro). Covers @cometchat/calls-sdk-javascript install, dual-SDK init (Chat SDK + Calls SDK), getRTCToken, the kit's CometChatIncomingCall / CometChatOutgoingCall / CometChatOngoingCall components, CallButtons composition, getUserMedia permissions, browser TURN/STUN handling, and additive-vs-standalone modes.","compatibility":"React >= 18, Next.js >= 13, React Router v6/v7, Astro >= 4; @cometchat/calls-sdk-javascript ^5 (v5.0.0 stable shipped; pin to `@5` because the npm `latest` dist-tag still points at v4.2.6 — see §Install); @cometchat/chat-sdk-javascript ^4.x; @cometchat/chat-uikit-react ^6.x (additive mode)"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-react-calls"},"updatedAt":"2026-05-18T19:04:55.751Z"}}