@cometchat/skills 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,527 @@
1
+ ---
2
+ name: cometchat-react-patterns
3
+ description: "Framework-specific patterns for integrating CometChat React UI Kit v6 into React projects (Vite or CRA). Covers provider setup, routing, layout integration, env vars, and common pitfalls."
4
+ license: "MIT"
5
+ compatibility: "Node.js >=18; React >=18; Vite >=4 or react-scripts (CRA); @cometchat/chat-uikit-react ^6; @cometchat/chat-sdk-javascript ^4"
6
+ allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
7
+ metadata:
8
+ author: "CometChat"
9
+ version: "3.0.0"
10
+ tags: "chat cometchat react vite cra patterns provider routing integration"
11
+ ---
12
+
13
+ ## Purpose
14
+
15
+ This skill teaches Claude how to integrate CometChat into a React project that uses Vite or Create React App. These are client-only environments with no SSR, which simplifies integration significantly.
16
+
17
+ **Read these companion skills first:**
18
+ - `cometchat-core` -- initialization, login, CSS, provider pattern, anti-patterns
19
+ - `cometchat-components` -- component catalog and composition patterns
20
+ - `cometchat-placement` -- WHERE to put chat (route, modal, drawer, embedded)
21
+
22
+ This skill covers the HOW for React specifically: project detection, provider wiring, routing patterns, env var conventions, and React-specific gotchas.
23
+
24
+ ---
25
+
26
+ ## 1. Project detection
27
+
28
+ A project is a plain React project (not a meta-framework) when `package.json` has `react` and ONE of these bundlers, but NONE of the framework packages:
29
+
30
+ **Bundler indicators (must have one):**
31
+ - `vite` in `devDependencies` (Vite project)
32
+ - `react-scripts` in `dependencies` (Create React App)
33
+
34
+ **Framework exclusions (must have none):**
35
+ - `next` -- use the `cometchat-nextjs-patterns` skill instead
36
+ - `astro` -- use the `cometchat-astro-patterns` skill instead
37
+ - `@remix-run/react` or `react-router` (v7 with `react-router.config.ts`) -- use the `cometchat-react-router-patterns` skill instead
38
+
39
+ **Edge case:** If `react-router-dom` is present but `next`, `astro`, and `@remix-run/react` are absent, this IS a plain React project that uses React Router as a library. This skill applies.
40
+
41
+ **Detection code:**
42
+
43
+ ```bash
44
+ # Quick check from the project root
45
+ cat package.json | grep -E '"(vite|react-scripts|next|astro|@remix-run)"'
46
+ ```
47
+
48
+ ---
49
+
50
+ ## 2. CometChatProvider for React
51
+
52
+ React (Vite/CRA) is client-only, so there are no SSR concerns. The provider can be simple.
53
+
54
+ ### Full implementation
55
+
56
+ ```tsx
57
+ // src/providers/CometChatProvider.tsx
58
+ import React, { useEffect, useState, createContext, useContext } from "react";
59
+ import { CometChatUIKit, UIKitSettingsBuilder } from "@cometchat/chat-uikit-react";
60
+
61
+ interface CometChatContextValue {
62
+ isReady: boolean;
63
+ error: string | null;
64
+ }
65
+
66
+ const CometChatContext = createContext<CometChatContextValue>({
67
+ isReady: false,
68
+ error: null,
69
+ });
70
+
71
+ export const useCometChat = () => useContext(CometChatContext);
72
+
73
+ // Module-level state prevents both double-init AND double-login in React
74
+ // StrictMode. StrictMode mounts, unmounts, and remounts in development,
75
+ // which fires useEffect twice. Without guards, init runs twice (duplicate
76
+ // WebSocket connections) and login() is called a second time while the
77
+ // first is still in flight, which makes the SDK throw
78
+ // "Please wait until the previous login request ends."
79
+ let initialized = false;
80
+ let loginInFlight: Promise<unknown> | null = null;
81
+
82
+ async function ensureLoggedIn(
83
+ uid: string,
84
+ authToken?: string,
85
+ ): Promise<void> {
86
+ const existing = await CometChatUIKit.getLoggedinUser();
87
+ if (existing) return;
88
+ if (loginInFlight) {
89
+ await loginInFlight; // a prior mount already started login — reuse its promise
90
+ return;
91
+ }
92
+ loginInFlight = authToken
93
+ ? CometChatUIKit.loginWithAuthToken(authToken)
94
+ : CometChatUIKit.login(uid);
95
+ try {
96
+ await loginInFlight;
97
+ } finally {
98
+ loginInFlight = null;
99
+ }
100
+ }
101
+
102
+ interface CometChatProviderProps {
103
+ children: React.ReactNode;
104
+ }
105
+
106
+ export function CometChatProvider({ children }: CometChatProviderProps) {
107
+ const [isReady, setIsReady] = useState(false);
108
+ const [error, setError] = useState<string | null>(null);
109
+
110
+ useEffect(() => {
111
+ async function setup() {
112
+ try {
113
+ if (!initialized) {
114
+ initialized = true;
115
+
116
+ const settings = new UIKitSettingsBuilder()
117
+ .setAppId(import.meta.env.VITE_COMETCHAT_APP_ID)
118
+ .setRegion(import.meta.env.VITE_COMETCHAT_REGION)
119
+ .setAuthKey(import.meta.env.VITE_COMETCHAT_AUTH_KEY)
120
+ .subscribePresenceForAllUsers()
121
+ .build();
122
+
123
+ await CometChatUIKit.init(settings);
124
+ }
125
+
126
+ await ensureLoggedIn("cometchat-uid-1"); // DEVELOPMENT ONLY — see cometchat-production skill
127
+
128
+ setIsReady(true);
129
+ } catch (e) {
130
+ setError(String(e));
131
+ }
132
+ }
133
+
134
+ setup();
135
+ }, []);
136
+
137
+ if (error) {
138
+ return (
139
+ <div style={{ color: "red", padding: 16, fontFamily: "monospace" }}>
140
+ CometChat Error: {error}
141
+ </div>
142
+ );
143
+ }
144
+
145
+ if (!isReady) return null;
146
+
147
+ return (
148
+ <CometChatContext.Provider value={{ isReady, error }}>
149
+ {children}
150
+ </CometChatContext.Provider>
151
+ );
152
+ }
153
+ ```
154
+
155
+ ### Where to mount
156
+
157
+ Mount `CometChatProvider` in the app's entry point, wrapping either the entire app or the router:
158
+
159
+ ```tsx
160
+ // src/main.tsx (Vite)
161
+ import React from "react";
162
+ import ReactDOM from "react-dom/client";
163
+ import App from "./App";
164
+ import { CometChatProvider } from "./providers/CometChatProvider";
165
+ import "@cometchat/chat-uikit-react/css-variables.css";
166
+ import "./index.css";
167
+
168
+ ReactDOM.createRoot(document.getElementById("root")!).render(
169
+ <React.StrictMode>
170
+ <CometChatProvider>
171
+ <App />
172
+ </CometChatProvider>
173
+ </React.StrictMode>
174
+ );
175
+ ```
176
+
177
+ For CRA, the pattern is identical but the file is `src/index.tsx` and uses `createRoot` the same way.
178
+
179
+ ### Production login variant
180
+
181
+ For production, replace the hardcoded `login("cometchat-uid-1")` with token-based auth. Fetch a token from your backend and use `CometChatUIKit.loginWithAuthToken(token)`. See the `cometchat-core` skill, section 2 (Login), for the full production auth pattern.
182
+
183
+ ---
184
+
185
+ ## 3. Routing integration
186
+
187
+ React projects handle routing in different ways. Detect which pattern the project uses, then integrate accordingly.
188
+
189
+ ### How to detect the routing pattern
190
+
191
+ ```bash
192
+ # Check for React Router
193
+ grep -r "react-router-dom" package.json
194
+ # Check for router usage patterns
195
+ grep -rn "createBrowserRouter\|BrowserRouter\|<Routes" src/ --include="*.tsx" --include="*.jsx" 2>/dev/null | head -5
196
+ ```
197
+
198
+ ### Pattern A: React Router v6 with createBrowserRouter
199
+
200
+ This is the modern recommended pattern. The router is defined as a data structure.
201
+
202
+ ```tsx
203
+ // src/router.tsx (or wherever the router is defined)
204
+ import { createBrowserRouter } from "react-router-dom";
205
+ import Layout from "./components/Layout";
206
+ import HomePage from "./pages/HomePage";
207
+ import ChatPage from "./pages/ChatPage"; // <-- new
208
+
209
+ export const router = createBrowserRouter([
210
+ {
211
+ path: "/",
212
+ element: <Layout />,
213
+ children: [
214
+ { index: true, element: <HomePage /> },
215
+ { path: "messages", element: <ChatPage /> }, // <-- add this route
216
+ // ... existing routes
217
+ ],
218
+ },
219
+ ]);
220
+ ```
221
+
222
+ ### Pattern B: React Router v6 with JSX Routes
223
+
224
+ Older pattern using `<Routes>` and `<Route>` elements:
225
+
226
+ ```tsx
227
+ // Inside App.tsx or wherever routes are defined
228
+ import { Routes, Route } from "react-router-dom";
229
+ import ChatPage from "./pages/ChatPage";
230
+
231
+ function App() {
232
+ return (
233
+ <Routes>
234
+ <Route path="/" element={<Layout />}>
235
+ <Route index element={<HomePage />} />
236
+ <Route path="messages" element={<ChatPage />} /> {/* add this */}
237
+ </Route>
238
+ </Routes>
239
+ );
240
+ }
241
+ ```
242
+
243
+ ### Pattern C: No router (single-page app)
244
+
245
+ Some React projects have no router at all. Chat is shown conditionally:
246
+
247
+ ```tsx
248
+ // App.tsx
249
+ import { useState } from "react";
250
+ import ChatPage from "./pages/ChatPage";
251
+
252
+ function App() {
253
+ const [showChat, setShowChat] = useState(false);
254
+
255
+ if (showChat) {
256
+ return (
257
+ <div>
258
+ <button onClick={() => setShowChat(false)}>Back</button>
259
+ <ChatPage />
260
+ </div>
261
+ );
262
+ }
263
+
264
+ return (
265
+ <div>
266
+ {/* existing app content */}
267
+ <button onClick={() => setShowChat(true)}>Open Messages</button>
268
+ </div>
269
+ );
270
+ }
271
+ ```
272
+
273
+ For projects without a router, consider suggesting `react-router-dom` if the project has multiple "pages." But do not force it -- some apps are intentionally single-page.
274
+
275
+ ### Full chat page component
276
+
277
+ See the `cometchat-placement` skill for complete `ChatPage` implementations (two-pane, full messenger, single thread). The page component itself is framework-agnostic -- the React-specific part is only how the route is wired.
278
+
279
+ ---
280
+
281
+ ## 4. Layout integration
282
+
283
+ ### Finding the layout
284
+
285
+ Read the project's source to find the component that renders the navigation. Common locations:
286
+
287
+ ```bash
288
+ # Find likely layout/nav files
289
+ find src \( -name "*.tsx" -o -name "*.jsx" \) | xargs grep -l "nav\|Nav\|Sidebar\|Header" 2>/dev/null | head -10
290
+ ```
291
+
292
+ Common patterns:
293
+ - `src/App.tsx` with inline nav + `<Outlet />`
294
+ - `src/components/Layout.tsx` wrapping children
295
+ - `src/layouts/MainLayout.tsx` or `src/layouts/AppLayout.tsx`
296
+ - `src/components/Navbar.tsx` or `src/components/Header.tsx`
297
+
298
+ ### Adding a navigation link
299
+
300
+ Once you find the nav, add a "Messages" link alongside existing links. Match the existing style:
301
+
302
+ ```tsx
303
+ // If the project uses React Router's <Link>:
304
+ import { Link } from "react-router-dom";
305
+
306
+ // In the nav component, alongside existing links:
307
+ <Link to="/messages">Messages</Link>
308
+
309
+ // If the project uses React Router's <NavLink> for active styling:
310
+ import { NavLink } from "react-router-dom";
311
+
312
+ <NavLink to="/messages" className={({ isActive }) => isActive ? "active" : ""}>
313
+ Messages
314
+ </NavLink>
315
+ ```
316
+
317
+ ### Adding a chat drawer/modal trigger
318
+
319
+ If the placement is a drawer or modal instead of (or in addition to) a route, add a trigger button to the nav:
320
+
321
+ ```tsx
322
+ // In the nav component
323
+ import { useState } from "react";
324
+ import { ChatDrawer } from "../components/ChatDrawer";
325
+
326
+ function Navbar() {
327
+ const [showChat, setShowChat] = useState(false);
328
+
329
+ return (
330
+ <nav>
331
+ {/* existing nav links */}
332
+ <button onClick={() => setShowChat(true)}>
333
+ Messages
334
+ </button>
335
+ <ChatDrawer isOpen={showChat} onClose={() => setShowChat(false)} />
336
+ </nav>
337
+ );
338
+ }
339
+ ```
340
+
341
+ See `cometchat-placement` for the full `ChatDrawer` and `ChatModal` implementations.
342
+
343
+ ---
344
+
345
+ ## 5. Environment variables
346
+
347
+ ### Vite projects
348
+
349
+ Create a `.env` file in the project root:
350
+
351
+ ```env
352
+ VITE_COMETCHAT_APP_ID=your_app_id_here
353
+ VITE_COMETCHAT_REGION=us
354
+ VITE_COMETCHAT_AUTH_KEY=your_auth_key_here
355
+ ```
356
+
357
+ **Access in code:** `import.meta.env.VITE_COMETCHAT_APP_ID`
358
+
359
+ Vite only exposes variables prefixed with `VITE_` to client-side code. Variables without this prefix are server-only (available in `vite.config.ts` but not in components).
360
+
361
+ **Important:** Vite's `.env` is NOT gitignored by default. Add `.env` to `.gitignore`:
362
+
363
+ ```bash
364
+ echo ".env" >> .gitignore
365
+ ```
366
+
367
+ ### CRA projects
368
+
369
+ Create a `.env` file in the project root:
370
+
371
+ ```env
372
+ REACT_APP_COMETCHAT_APP_ID=your_app_id_here
373
+ REACT_APP_COMETCHAT_REGION=us
374
+ REACT_APP_COMETCHAT_AUTH_KEY=your_auth_key_here
375
+ ```
376
+
377
+ **Access in code:** `process.env.REACT_APP_COMETCHAT_APP_ID`
378
+
379
+ CRA requires the `REACT_APP_` prefix for client-side variables. CRA requires a restart after changing `.env` files (Vite does not).
380
+
381
+ ### TypeScript type hints (Vite only)
382
+
383
+ For better IDE support, add CometChat env vars to `src/vite-env.d.ts`:
384
+
385
+ ```typescript
386
+ /// <reference types="vite/client" />
387
+
388
+ interface ImportMetaEnv {
389
+ readonly VITE_COMETCHAT_APP_ID: string;
390
+ readonly VITE_COMETCHAT_REGION: string;
391
+ readonly VITE_COMETCHAT_AUTH_KEY: string;
392
+ }
393
+
394
+ interface ImportMeta {
395
+ readonly env: ImportMetaEnv;
396
+ }
397
+ ```
398
+
399
+ ### Production auth keys
400
+
401
+ The `AUTH_KEY` is for development only. In production, you need a backend that generates auth tokens. Plain React (Vite/CRA) projects have no built-in server, so you need either:
402
+ - A separate backend (Express, Fastify, etc.)
403
+ - A serverless function (Vercel, Netlify, AWS Lambda)
404
+ - A BaaS like Firebase or Supabase with a Cloud Function
405
+
406
+ The backend calls CometChat's REST API with your `AUTH_TOKEN` (server-side secret, never exposed to client) to generate per-user auth tokens. See the `cometchat-core` skill, section 2, for the flow.
407
+
408
+ ---
409
+
410
+ ## 6. CSS import
411
+
412
+ Import CometChat's CSS variables exactly once, at the app root. For React (Vite/CRA), the right place is `src/main.tsx` (or `src/index.tsx` for CRA):
413
+
414
+ ```tsx
415
+ // src/main.tsx
416
+ import "@cometchat/chat-uikit-react/css-variables.css";
417
+ import "./index.css"; // your app's styles AFTER the CometChat import
418
+ ```
419
+
420
+ Alternatively, use a CSS `@import` in your root stylesheet:
421
+
422
+ ```css
423
+ /* src/index.css */
424
+ @import "@cometchat/chat-uikit-react/css-variables.css";
425
+
426
+ /* your styles below */
427
+ ```
428
+
429
+ ### Import order matters
430
+
431
+ CometChat's `css-variables.css` defines `:root` CSS custom properties. Your overrides must come AFTER this import to take effect:
432
+
433
+ ```css
434
+ /* CORRECT: overrides come after */
435
+ @import "@cometchat/chat-uikit-react/css-variables.css";
436
+
437
+ :root {
438
+ --cometchat-primary-color: #6851d6;
439
+ }
440
+ ```
441
+
442
+ ```css
443
+ /* WRONG: overrides come before -- they will be overwritten */
444
+ :root {
445
+ --cometchat-primary-color: #6851d6;
446
+ }
447
+
448
+ @import "@cometchat/chat-uikit-react/css-variables.css";
449
+ ```
450
+
451
+ ---
452
+
453
+ ## 7. Common pitfalls
454
+
455
+ ### StrictMode double-init
456
+
457
+ React's `StrictMode` (used by default in Vite and CRA development mode) intentionally double-invokes effects. Without the module-level `initialized` flag in the provider, `CometChatUIKit.init()` runs twice. The second call may silently fail or create a second WebSocket connection.
458
+
459
+ The `initialized` flag in the provider pattern (section 2) handles this. Never remove it. It is not a hack -- it is the correct pattern for one-time SDK initialization in React.
460
+
461
+ ### CSS import duplication
462
+
463
+ If `css-variables.css` is imported in multiple files (e.g., both `main.tsx` and `ChatPage.tsx`), CSS custom properties are declared twice. This usually works but can cause issues with specificity if the imports are processed in different order by the bundler. Import exactly once at the root.
464
+
465
+ ### Hot Module Replacement (HMR)
466
+
467
+ Vite's HMR replaces modules without a full page reload. CometChat's SDK holds a WebSocket connection that survives HMR. This is generally fine -- the SDK connection persists and chat keeps working.
468
+
469
+ However, if you change the provider file itself during development, HMR may re-execute the module. The `initialized` flag prevents double-init, but the WebSocket connection from the previous module instance may linger. If you see duplicate messages or connection issues during development, do a full page reload (`Ctrl+Shift+R`).
470
+
471
+ ### Container height
472
+
473
+ CometChat components fill 100% of their container. The most common visual bug is components rendering with zero height because their container has no explicit dimensions. Always ensure the chat container has a height:
474
+
475
+ ```tsx
476
+ /* CORRECT: explicit height */
477
+ <div style={{ height: "100vh" }}>
478
+ <CometChatConversations ... />
479
+ </div>
480
+
481
+ /* CORRECT: flex layout with bounded parent */
482
+ <div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
483
+ <nav>...</nav>
484
+ <div style={{ flex: 1 }}>
485
+ <CometChatConversations ... />
486
+ </div>
487
+ </div>
488
+
489
+ /* WRONG: no height constraint -- component collapses to zero */
490
+ <div>
491
+ <CometChatConversations ... />
492
+ </div>
493
+ ```
494
+
495
+ ### Vite dependency optimization
496
+
497
+ Vite pre-bundles dependencies for faster dev startup. CometChat's packages are large and may trigger Vite's "new dependency found, reloading" message on first load. This is normal and only happens once. If it causes issues, you can pre-include the packages:
498
+
499
+ ```typescript
500
+ // vite.config.ts
501
+ export default defineConfig({
502
+ optimizeDeps: {
503
+ include: [
504
+ "@cometchat/chat-uikit-react",
505
+ "@cometchat/chat-sdk-javascript",
506
+ ],
507
+ },
508
+ });
509
+ ```
510
+
511
+ ---
512
+
513
+ ## 8. Complete integration checklist
514
+
515
+ When integrating CometChat into a React (Vite/CRA) project, follow these steps in order:
516
+
517
+ 1. Install packages: `npm install @cometchat/chat-uikit-react @cometchat/chat-sdk-javascript`
518
+ 2. Create `.env` with `VITE_COMETCHAT_APP_ID`, `VITE_COMETCHAT_REGION`, `VITE_COMETCHAT_AUTH_KEY`
519
+ 3. Add `.env` to `.gitignore` if not already there
520
+ 4. Import `@cometchat/chat-uikit-react/css-variables.css` in `src/main.tsx`
521
+ 5. Create `src/providers/CometChatProvider.tsx` (section 2)
522
+ 6. Mount `CometChatProvider` in `src/main.tsx` wrapping `<App />`
523
+ 7. Create the chat page component (see `cometchat-placement` for patterns)
524
+ 8. Wire the route (section 3) or trigger (section 4)
525
+ 9. Add a "Messages" link to the existing nav
526
+
527
+ **Do not skip step 6.** The provider must wrap the app root so init happens once, regardless of which route or modal opens chat.