The CometChat React UI Kit provides pre-built, customizable UI components that developers can use to quickly integrate real-time messaging and calling into any React application.
Check out the Sample App to see the UI Kit in action with a fully functional chat experience including conversations, messaging, calling, and AI features.
- Node.js >= 18
- npm >= 9
- React >= 18
- Register at the CometChat Dashboard to create an account.
- Create a new app to get your App ID, Region, and Auth Key.
npm install @cometchat/chat-uikit-react- Conversations — List and manage one-on-one and group chats
- Messages — Rich message list with reactions, threads, and read receipts
- Calling — Voice and video calling with call logs
- AI Features — AI-powered assistant chat and smart replies
- Theming — CSS custom properties for full visual customization
- Localization — Built-in multi-language support
The project has three test suites: unit tests (Vitest), E2E tests (Playwright), and Storybook.
Unit tests live alongside the source files as *.spec.ts / *.spec.tsx and cover individual components, hooks, and utilities.
Run all unit tests (single pass):
npm testRun with coverage:
npm run test:coverageNo browser or running server is required.
Storybook provides an interactive development environment for building and testing components in isolation.
Start Storybook (dev mode):
npm run storybookOpens at http://localhost:6006.
Build static Storybook:
npm run build-storybookEnd-to-end tests live in sample-app/e2e/ and run against the live sample app. They cover full user journeys: login, messaging, reactions, threads, groups, calls, search, and more.
Create a new CometChat app (or use an existing one dedicated to E2E testing).
- Go to CometChat Dashboard
- Note down: App ID, Region, Auth Key, REST API Key
In the CometChat Dashboard, enable:
- Stickers (also add sticker packs from settings)
- Calling
- Conversation & Advanced Search
- Polls
- Collaborative Document
- Collaborative Whiteboard
Then, under Chat & Messaging → Settings → General Configuration → Media URL Access, select Presigned URLs
Required for every attachment flow —
e2e/multi-attachment, thee2e/message-composerattachment tests, and the image test ine2e/thread-subscription. Without it the upload tray never resolves and those tests time out.
In the Dashboard → BYO Agents:
- UID:
ai-agent-e2e - Name:
AI Agent E2E - Role:
@agentic
cd sample-app
cp .env.e2e.example .env.e2eFill in .env.e2e:
COMETCHAT_APP_ID=your-app-id
COMETCHAT_REGION=us
COMETCHAT_AUTH_KEY=your-auth-key
COMETCHAT_API_KEY=your-rest-api-key
AI_AGENT_UID=ai-agent-e2e.env.e2e.example is the full reference — it also carries the optional per-suite flags below, each of which self-skips its tests when left blank:
| Var | Suite |
|---|---|
E2E_BLOCKED_MIME_TYPE |
multi-attachment (blocked-MIME test) |
E2E_PIN_MESSAGES_LIMIT, E2E_PARTICIPANT_PIN_DENIED, E2E_PARTICIPANT_UNPIN_DENIED, E2E_PARTICIPANT_PIN_LISTING_DENIED |
pin & save (see below) |
GROUP_AGENT_1_UID, GROUP_AGENT_2_UID, AGENTIC_GROUP_1_ID, AGENTIC_GROUP_2_ID |
agentic group chat |
npx playwright installSeed test data (one-time, idempotent):
cd sample-app
npm run e2e:seedStart dev server (keep running in separate terminal):
npm run devRun all E2E tests (headless):
npm run e2eRun with Playwright UI (interactive):
npm run e2e:uiRun headed (visible browser):
npm run e2e:headedRun in debug mode:
npm run e2e:debugRun a specific test file:
npx playwright test e2e/conversations/conversations.spec.tsTo delete all E2E-created data and reset the app:
npm run e2e:cleanup| Chat | Purpose | Modified by Tests? |
|---|---|---|
| Bob Smith (e2e-user-2) | Static 1:1 — read-only tests | ❌ Never |
| Design Team (e2e-group-1) | Static group — pagination, group details | ❌ Never |
| Strategy (e2e-group-35) | Mutable group — send/edit/delete/thread/reactions | ✅ Yes |
| CI/CD (e2e-group-33) | Incoming messages for message-privately / pin-conversation tests | ❌ Never - Unless the tests send incoming messages |
| Engineering | Mutable group — the chat the pin-conversation tests pin/unpin | ✅ Yes |
| Testing | Secondary mutable group — use for anything (currently the pin-conversation "stays below pinned" test) | ✅ Yes |
| AI Agent E2E | AI assistant chat tests | ✅ Yes |
Engineering & Testing groups — create two public groups named exactly
Engineeringand exactlyTesting(any guids). Adde2e-user-1to Engineering (the tests send it a message ase2e-user-1to bring it into the list, then pin it) ande2e-user-2to Testing (used to send an incoming). The suite resolves both by name at runtime, so no guids go in env. TreatTestingas a free secondary group for future tests.
The pin/save suites (e2e/pin-messages, e2e/save-messages, and the "Pin conversation" block in e2e/conversations) need these enabled on the E2E app:
-
Enable in the dashboard:
features.ux.messages.pinned.enabled,features.ux.messages.saved.enabled,features.ux.conversations.pinned.enabled. -
Strategy roles:
e2e-user-1is owner/admin;e2e-user-2is a participant. The UI Kit shows Pin/Unpin to every member and the server enforces who may act — so the permission test asserts the participant is offered Pin message (the deny surfaces as a toast, covered below). -
Pin-message limit test self-skips unless
E2E_PIN_MESSAGES_LIMIT=5(it's only meaningful at a small cap). Set it to yourfeatures.ux.messages.pinned.limit. -
Pin-permission deny tests (denied pin / denied unpin / denied pinned-listing) each self-skip until you both (a) apply the matching deny to the participant scope in the Strategy group and (b) set the flag. Apply these in the dashboard after seeding — they act on the Strategy group, so it must already exist:
- Deny the participant scope pin → set
E2E_PARTICIPANT_PIN_DENIED=1. - Deny the participant scope unpin → set
E2E_PARTICIPANT_UNPIN_DENIED=1. - Deny the participant scope pinned-messages listing → set
E2E_PARTICIPANT_PIN_LISTING_DENIED=1.
Leave a flag blank to skip its test. (The deny lives in dashboard config, not seeding, so an un-configured app would let the action through and the test would fail for the wrong reason — hence the self-skip.) There are also three permutation tests that need a specific mix of allowed/denied — a denied listing must not append a realtime pin (pin allowed, listing denied); a denied pin must not reach the list (pin denied, listing allowed); a denied unpin keeps the message listed (unpin denied, pin & listing allowed). Because they need different mixes, the full permission matrix spans more than one env/dashboard configuration — run them in separate passes.
- Deny the participant scope pin → set
-
Each test clears its own pinned/saved state over REST first, so the low pin/save caps never leak between tests. Conversation-pin tests system-pin Strategy and pin only the Engineering chat (Bob stays a read-only fixture).
e2e/thread-subscription needs no dashboard flag of its own, but:
- Stickers and Polls extensions must be enabled (step 2) — the "auto-subscribe by message type" tests send one of each.
- Presigned URLs must be on (step 2) — one test sends an image.
- Runs against Strategy (group cases) and the Bob Smith 1:1 — thread subscription is offered in 1:1 as well as groups, so the 1:1 case asserts the option is present.
Two tests in e2e/multi-attachment/multi-attachment.spec.ts exercise the composer's
rejected (non-retryable) upload states. They need inputs that are impractical to
commit (a huge file) or that depend on dashboard config, so they skip themselves
unless you add a matching fixture to e2e/fixtures/. The lookup is by name prefix,
so the extension is up to you (within the limits noted below).
1. File size exceeded — e2e-oversize.*
Any single file larger than 100 MB (any extension) in e2e/fixtures/. You can generate one with:
# from sample-app/
head -c 150000000 /dev/urandom > e2e/fixtures/e2e-oversize.txt # ~150 MB2. File type not supported — e2e-blocked-mime.* + E2E_BLOCKED_MIME_TYPE
This test needs both of the following — if either is missing it is skipped:
- A fixture named
e2e-blocked-mime.*ine2e/fixtures/. Its extension must not bepng,mp4,mp3, orpdf(so it can't collide with the standard fixtures). The file's bytes are what get uploaded; the extension itself doesn't matter beyond that. E2E_BLOCKED_MIME_TYPEin.env.e2e, set to the exact MIME type you denied in the dashboard (e.g.E2E_BLOCKED_MIME_TYPE=image/svg+xml). The test attaches this MIME to the upload explicitly. This is required because Playwright can't infer a MIME for exotic extensions and would send an empty one — the server then rejects withmimeType is required(a retryable failure) instead of the permission-denied rejected state the test asserts. There is no fallback: the MIME must come from this env var and must match what you denied.
Where to set the restriction: on the Strategy group → Scope permissions → Admin
role. The E2E suite runs against the Strategy group and the e2e user e2e-user- is normally an
admin/owner there, so blocking the MIME type for the Admin scope reliably applies to the
uploads under test.
cd sample-app
cp .env.e2e.example .env.e2e # Fill in credentials
npx playwright install # Install browsers (once)
npm run e2e:seed # Seed test data (once)
npm run dev # Start dev server
npm run e2e # Run testsFor issues running the project or integrating with our UI Kits, consult our documentation or create a support ticket or seek real-time support via the CometChat Dashboard.

