Skip to main content
This is the single reference an AI coding agent follows to build a real messaging experience with CometChat — not a bare component drop, but an app with the polish of the messengers people use every day, on the customer’s own brand. It is platform-generic: the same principles, tokens, motion, and recipes apply to every CometChat UI Kit and SDK — React, Angular, React Native, iOS, Android (Kotlin XML views and Jetpack Compose), and Flutter, plus the headless Chat/Calls SDKs for fully custom UI. (The Vue UI Kit is on an older generation, v4: the process and design rules apply, but check its docs for what it supports before relying on any component named here.) Process, design, and behavior rules are shared; the platform-specific parts — theming mechanics, layout, navigation, and gotchas — are in Per-platform notes. It defines the prerequisites and process, the UX principles, the design tokens, the motion and responsiveness, the component mapping, and per-app-type recipes, plus how to build a fresh app or retrofit an existing one end-to-end without breaking it. When the user asks for a clone of an existing app, it also defines how to research that app down to the smallest detail, how to decide per feature whether a UI Kit component or your own UI is the right tool, and how to make the kit components and your own UI look and behave as one design that matches the target. Use it with the product-scoped API indexes: this blueprint says what to build and how it should feel; the exact component props, theme keys, and SDK methods come from the live docs (see Where to get exact APIs). Never invent an API — fetch it.

Before you start (prerequisites)

Confirm these first — the same steps apply to every platform (React, Angular, React Native, iOS, Android, Flutter, and Vue v4) and to the headless Chat/Calls SDKs:
  • A CometChat app + credentials in place. App ID, Region, and Auth Key (dev) — read from the app’s env or config; production uses server-minted auth tokens. Never invent credentials; if none are found, write placeholders and leave “add your credentials” as an action item.
  • The target platform/family. Detect it from the project (or ask), then install that UI Kit — or the headless SDK if the design needs fully custom UI. Everything below is family-agnostic; get the exact component and theme names from that family’s docs.
  • The archetype and feature scope. Map the request to an app recipe and the full feature checklist — but confirm scope through Discovery and requirements first. If unclear, default to a consumer 1:1 + group messenger.
  • The customer’s brand. Logo, app name, and palette — these replace the brand tokens; CometChat is the engine, not the identity.
  • Fresh build vs retrofit. Fresh: a working app shell + the family’s toolchain. Retrofit: the app builds today, a clean git branch, and an inventory of the current chat/UI, routing, state, and auth so the swap stays non-breaking.
  • Docs access. The family’s scoped LLM index plus the Docs MCP (or the docs site), so every prop and method is fetched, not guessed.

How an agent must use this

Trigger this blueprint whenever the request implies a chat experience, not just a widget — e.g. “build a full messaging app”, “chat that looks and feels real”, “a modern messenger”, “team chat with channels”, or naming a well-known app to emulate.

From a one-line request

Users rarely spell out a process. “Build me a clone of X”, “make a chat like X”, or “an app like X with CometChat” is a complete request: run everything on this page yourself — the user should never have to tell you to research, choose build levels, write requirements, compare, or verify. Treat the named app as the clone target and go straight into Cloning an existing app. Ask once, briefly, then work. Send at most one short message of questions, only for what you can’t detect, each with a default so “go with the defaults” is a valid answer:
  • Platform / framework — skip if there’s a project in the working directory (detect it from the project files). Default for a new web app: React + Vite with the current React UI Kit.
  • App name — the customer’s product name to show instead of the target’s. Default: a neutral placeholder they can rename.
  • Screenshots (optional) — say that a few screenshots of the target’s main screens, in light and dark, noticeably improve how close the clone gets.
Everything else takes these defaults unless the user says otherwise: One approval, then build. Write cometchat-requirements.md, and in chat give a short summary — the proposed scope, the items that need the user’s decision (L5 / L6, unverified details, dashboard actions), and the file path — rather than pasting the whole table. A single “go” approves the proposed scope; the user can cut or add items in the file instead. Then build, compare, and verify item by item exactly as the rest of this page describes, and report against the plan at the end.
  1. Run discovery first — don’t jump to code. Ask the required questions, research the target experience, and produce a requirements file + checklist the user approves before building. See Discovery and requirements. This fixes the archetype (default: a consumer 1:1 + group messenger) and the third-party-package policy. If the user names an app to clone, research that app first — every screen, behavior, and visual detail, down to the tiny ones — and match how it works, not just how it looks. See Cloning an existing app.
  2. Decide, per item, kit component or your own UI. Use the UI Kit wherever it can reach the target’s behavior and look; build your own where it can’t. Record the decision and the reason for every item. See Kit component or your own UI?.
  3. Build the auth/session gate first — a real login screen and session, never a hardcoded UID. See Auth and session. Logged-out shows login; logged-in shows the app; logout works.
  4. Wire the complete screen set — not just a list + a message pane. Chats, chat, contact/group details, search, new chat, calls, settings must all exist and navigate, and each opens where and how the target opens it (dialog, popover, side panel, full screen). See Navigation and screens. Every control the UI shows must open a working screen — a dead button (search/details/attach/call that does nothing) or a half-wired pane is a bug, not “done.”
  5. Get the layout right — fill the viewport, the message list scrolls internally, the composer is pinned, there is no empty gap and no over-scroll, and it’s keyboard-safe on mobile. See Layout correctness.
  6. Apply the design tokens via the family’s real theming API — not the flat defaults. See Design tokens and Premium look. Map roles → the family’s theme keys; theme through the kit, not ad-hoc CSS that fights it. When cloning, the tokens are measured from the target, and the kit components and your own components share them so the app reads as one design. See Design sync.
  7. Enable the features from the full feature checklist — some are props, some are Dashboard settings. Implement the archetype’s whole set, not a subset.
  8. Apply motion (Motion and animation) so send/receive/typing/reactions/ calls feel native — and, when cloning, like the target.
  9. Keep the customer’s brand — replace brand tokens + assets only; never ship CometChat branding as the app’s identity (Brand ownership).
  10. Verify against the acceptance checklist before finishing — a basic-looking UI, a dead control, an empty scroll gap, a missing login, or (for a clone) a behavior or visual that differs from the target without being agreed all fail the check.

Grounding rules (non-negotiable)

  • APIs come from the docs, never memory. Fetch component props/theme keys/SDK methods from the family’s scoped index and page .md twins. If a prop can’t be verified, don’t use it.
  • Theme through the kit, not around it. Use the family theming API so light/dark, RTL, and updates keep working. Ad-hoc CSS overrides on kit internals break on upgrade.
  • Brand is the customer’s. Their logo, name, and palette — CometChat is the engine, not the skin.
  • Non-destructive when retrofitting. Work on a branch; preserve routing, state, and auth; swap region-by-region; delete only the UI you replace. See Retrofitting.

Discovery and requirements (do this first)

Do not start building before the user approves a plan, and do not silently build “the core” and call it done. When the user asks for an app (especially “make it like X”), your first deliverable is a requirements + plan of action they approve — then you build against an approved checklist. This is what separates a real app from a component drop. For a short request (“clone X”, “an app like X”), discovery is the flow in From a one-line request: one message of questions with defaults, a proposed scope in cometchat-requirements.md, and one approval. The defaults there answer the questions below; the user can override any of them. For a longer or open-ended request, work through the steps below. Four rules that are not optional:
  1. The plan must be exhaustive. List every feature, behavior, and UI detail needed to match the target — not just the headline features. Include the small parity details that make or break “it feels like X”: receipts shown or hidden, channel/DM iconography (e.g. # / lock vs avatar), threads, mentions variants, composer layout/alignment, empty/loading states, density, dark mode. Missing an item in the plan is the failure mode to avoid — aim to miss nothing. (The user can point out anything you missed; your job is that they rarely have to.)
  2. Scope is the user’s decision, never yours. Present the whole plan with each item tagged by how it’s built and its effort, then let the user choose core-only, a subset, or everything. You may propose a default scope (as the one-line flow does), but it is only a proposal until they approve it. Do not pre-decide that “core” is enough, and do not ship a partial build as finished — say plainly what is and isn’t done against the plan.
  3. Tag feasibility honestly. Every item says how it’s built — native component · prop/config · dashboard extension · Calls SDK · custom code · third-party package · or not natively possible (bespoke/out). The user can’t choose scope well if “not native” items look free.
  4. Match behavior, not just looks. When the user names an app, “like X” means it works like X: features open in the same place (a centered dialog, a popover, a side panel, a new page), respond to the same gestures and shortcuts, and show the same states. Re-skinning the kit’s default layout in the target’s colors is not a clone. Where the kit’s default placement or flow differs from the target, the target wins — see Kit component or your own UI?.

1. Ask the required questions

Ask only what you can’t safely infer; propose sensible defaults and let the user confirm. At minimum:
  • What experience and scope? The kind of app and the must-have features (1:1, groups/channels, calls, reactions, threads, search, media/voice notes, presence, push, moderation, AI…). Map it to an archetype; default to a consumer 1:1 + group messenger if unspecified.
  • Third-party packages policy — it changes the whole build, so the user decides. Ask explicitly, or, in the one-line flow, apply the default below and list each package that would get closer as an option in the plan they approve:
    • UI Kit / SDK only — no third-party runtime dependencies; use only what the kit/SDK and the dashboard provide. (Safe default.)
    • Closest-possible fidelity — third-party packages allowed where the kit can’t reach a behavior (for example a Lottie player for animated emoji, a charting library). List each one for approval.
    • Middle ground — kit/SDK first, and ask per-feature before adding any dependency.
  • UI Kit vs headless SDK — prebuilt components, or a fully custom UI on the headless Chat/Calls SDK?
  • Platform and framework, auth model (dev Auth Key vs server-minted tokens), brand (logo, name, palette), form factors (mobile / tablet / desktop), dark mode, localization / RTL.
  • Plan-gated / dashboard features — which extensions (search, stickers, polls, GIFs, moderation…) the user will enable on their app, since several won’t work until they do (see Known UI Kit gotchas).

2. Research the feature set

Don’t rely on memory of what a given app “has.” Research it: read the relevant CometChat docs (the family’s scoped LLM index + the Docs MCP) for what the kit/SDK actually supports, and study the target experience the user named for the behaviors and screens it implies. Produce a concrete list — every screen, message type, interaction, animation, and parity detail in scope. Go down to the details that make it feel like the target, e.g.: does it show read receipts or not; how are channels vs DMs labelled (# / lock / avatar); are threads first-class; which mention types; how is the composer laid out; empty/loading/error states; density and dark palette. These are exactly the things that reveal a “themed default” instead of the real app, so they belong in the plan — not discovered later by the user. When the user names an app to clone, this step is the full research protocol in Cloning an existing app: an inventory of every screen, entry point, container, interaction, state, and visual token of the target.

3. Write a requirements file

Emit a cometchat-requirements.md at the project root capturing the whole plan. Tag every item with how it will be built and whether it’s in scope, so the user can approve or cut each one:
Every row the target has goes in the table — including the parity details from step 2 — so the user approves or cuts each with eyes open. A row that would be “not-native” must be labelled as such, not quietly dropped. The build levels are defined in Kit component or your own UI?; the “why” is one line (e.g. “kit search renders as a list panel; target groups results in a dialog — hosting the kit view in a dialog matches it”).

4. Get approval, then build one item at a time

Present the full plan and let the user pick the scope — core-only, a subset, or everything. It is their call, not yours; never assume “core” is enough. Once they sign off, turn the selected rows into a checklist and implement one item at a time, in order: build it, verify it (works + on-brand + no dead controls + matches the target’s behavior for that item), check it off, move to the next. Don’t batch the whole app in one pass. When you report progress, state it against the plan: what’s done, what’s still open, and what the user chose to leave out — never present a partial build as “finished.” If the user later spots a missing behavior, add it to the plan and treat that as a gap to avoid next time, not the normal way to find requirements.

Cloning an existing app (research every detail)

When the user says “build a clone of X”, “make it look and work like X”, or shares screenshots of an app, the target app is the spec. The quality of the clone is decided here, before any code: what isn’t researched gets filled in with kit defaults, and kit defaults are what make a clone feel wrong. This applies to any app — a consumer messenger, a team workspace, a community server, a support widget, a marketplace inbox — not to a particular brand.

Sources, best first

  1. What the user gives you: screenshots, screen recordings, a Figma file, or access to the real app. Ask for them if the target matters and you have none; a few screenshots of the main screens, in light and dark, settle more questions than any description.
  2. The real app, if you can open it (a web app in the browser tool), observed directly — click every control, hover, right-click, resize, and switch to dark mode.
  3. The target’s public help center and docs, which describe features, shortcuts, and settings precisely.
  4. Your own knowledge of the app — only to know where to look. Anything you can’t confirm from sources 1–3 is marked unverified in the plan and confirmed with the user, never silently filled with a guess or a kit default.

The inventory (go down to the tiny details)

Record each of these for the target, per screen and per form factor (mobile / tablet / desktop):
  • Screens and navigation. Every screen and how you get to it; what stays on screen while another opens (does the chat stay visible behind a dialog?); back / close behavior; deep links and URL changes on web.
  • Entry point → container → close, for every feature. Where it starts (button, field, menu item, keyboard shortcut, gesture), what it opens in (centered dialog, popover, dropdown, side panel, bottom sheet, new page, inline expansion), and how it closes (Esc, click outside, back, close button, after an action). This is the detail most clones get wrong: the same feature opening in a different place makes the whole app feel different.
  • Lists and rows. Grouping and section headers (collapsible? ordering?), what each row shows (avatar shape, name, preview, time format, unread style — bold, dot, count badge, mention badge), hover and selected states, row actions (swipe, hover icons, context menu), and sort order.
  • Messages. Bubble or flat layout, alignment of own messages, grouping rules, avatar and name placement, timestamp placement and format, edited / deleted markers, receipts (shown or not, style), reactions placement, reply / quote style, thread entry (“N replies” link, avatars), link previews, media layout, system messages. Record avatar, name and alignment for your own messages and others’, in a 1:1 chat and in a group, and write the kit’s default next to the target’s. The defaults often differ: a kit may show the avatar and name only on other people’s messages in groups, while team apps show both on every message. When a kit setting doesn’t cover a case (often 1:1 chats and your own messages), customize the message’s leading view (avatar) and header view (name) through the kit’s slots. Do it without asking; never ship the kit default instead.
  • Calls. Where a call starts (header buttons, a channel “huddle” button), whether it rings, and where the live call shows: full screen, a docked bar, a floating mini window, or inline. See Calls and huddles.
  • Composer. Layout, placeholder text, toolbar (formatting, attachments, emoji, mentions, voice), send button vs Enter, Shift+Enter, multiline growth, drafts, slash commands, what happens on paste and drag-and-drop.
  • Interactions. Hover toolbars, long-press menus, right-click menus, swipe actions, keyboard shortcuts (open search, switch conversation, edit last message, mark read, close), focus order, scroll behavior (jump to unread, load older), double-click / double-tap behaviors.
  • States. Empty, loading (skeletons or spinners), error, offline / reconnecting, no results, permission denied, first-run / onboarding.
  • Copy. Labels, placeholders, menu items, confirmation text, empty-state text, date and time formats. Copy the style and wording pattern; use the customer’s product name, not the target’s.
  • Visual tokens, measured. Colors (sample them from screenshots), font family and the type scale, spacing, radii, borders vs shadows, icon set style (outline / filled, stroke width), avatar shapes and sizes, density, light and dark palettes. See Design sync.
  • Secondary surfaces. Not only the main screens: the thread panel and its header, message info (read by), the reactions list, emoji and sticker pickers, context and hover menus, confirm dialogs, toasts, the image / video viewer, the forward picker and call screens. Each one is its own row with the target’s design. Users judge a clone by these, and the kit opens several of them by itself (the hover toolbar opens message info; a reply count opens the thread). Theme each one to the target, rebuild it, or hide the option that opens it. A kit default left in place is not acceptable.
  • Motion, as its own list. What animates and where: animated emoji in emoji-only messages, reactions and the reaction bar, how a sent message enters, menu and panel transitions, the typing indicator. Each entry is a row with a level. Animated emoji art the target owns is not natively possible; use an open set or say it’s static.
  • Responsive behavior. What collapses, hides, or becomes a separate screen at each width.
  • Accessibility. Keyboard reachability, focus rings, screen-reader labels the target exposes.
Put every item in the requirements table with its observed behavior in the Target behavior column. A detail you didn’t research doesn’t go in as a default — it goes in as unverified with a question for the user. Clone the structure, behavior, and interaction patterns. Don’t copy the target’s logo, name, trademarked artwork, illustrations, sounds, or proprietary icon set; use the customer’s brand and an open icon set of the same style. Colors, type, spacing and radii default to the target’s, measured from it in light and dark, so the clone looks like the app the user named. If the customer wants their own brand colors instead, swap them into the same token source; layout and behavior stay the target’s.

Kit component or your own UI? (decide per item)

Make this decision per item in the requirements table, not once for the whole app. The UI Kit components carry a lot you don’t want to rebuild — real-time updates, pagination, receipts, typing, optimistic send, retries, accessibility, localization — so prefer them wherever they can reach the target. Build your own only where the target’s behavior or layout is out of the kit’s reach. Pick the lowest level that matches the target’s behavior and look: Rules:
  • Placement is never a reason to rebuild. If the kit component’s content matches and only where it opens differs, host it in your container (L3). Don’t accept the kit’s default placement either: if the target opens search as a centered dialog, a side panel is wrong even though it works.
  • Keep the message core on the kit when you can. The message list and composer carry the most real-time logic; reach the target through config, slots, and templates before replacing them. If you must replace one, rebuild the whole behavior on the SDK (listeners, receipts, pagination, retries), not just the visuals.
  • Mixing is normal. A typical clone is mostly L0–L3 with a handful of L4 pieces; that’s the goal, not a compromise.
  • Never patch kit internals. No forking the kit, no editing node_modules, no styling deep internal class names except the documented exceptions in Known UI Kit gotchas. If a slot or prop doesn’t exist for what you need, go up a level.
  • An L4 piece takes over everything the kit component did — call buttons, presence, unread counts, typing — or keeps the kit piece for those parts. Replacing a kit header with your own and losing the call buttons is how a clone ends up with “no calling”.
  • One owner per data set — no duplicate API calls. If a kit component is mounted for a list, your own component must not fetch the same list again: take the selection from the kit’s callbacks and pass objects down. When you replace a kit component, remove it rather than running both. Initialize and log in once (guard against React StrictMode’s double effects), register each real-time listener once with a unique ID and remove it on unmount, and never poll what a listener delivers.
  • Every L4 piece uses the same tokens as the themed kit, so users can’t tell which parts are kit and which are yours (see Design sync).
  • Verify the API before choosing the level. Whether a slot, prop, or SDK method exists comes from the family docs, not memory. If you can’t find it, the item is a level higher than you hoped.
  • Write the level and the reason in the requirements table, so the user sees which items are cheap (L0–L2) and which are real work (L4+) before choosing scope.

Calls and huddles

Calling is off until the platform’s Calls SDK is installed and enabled at init, so a clone that only themes the UI has no working call. Plan the call experience like any other feature:
  • 1:1 voice and video: the kit’s call buttons and its incoming / outgoing call screens.
  • Group calls don’t ring. Starting one posts a “meeting” message with a Join button in the group; members join from it.
  • Where the live call shows is a requirement, not a default. If the target shows it in a docked bar or a mini window and the kit’s call screen opens full screen, that screen is a kit default left in place. Join the call into your own container instead.
Huddle / drop-in room (a live audio session in a channel that others see and join, with no ringing). The same five steps on every platform:
  1. Start: a group call, audio-only, from the channel header. Send the same meeting message the kit’s group call button sends, so the channel shows a Join entry and the conversation list updates. Use the group’s id as the session id, so the whole channel shares one room.
  2. Join: replace the kit’s call bubble so Join opens your container, not the kit’s full-screen call screen.
  3. Dock: join the session into your own fixed-size container through the Calls SDK: generate the call token, then join with your container as the target. Mounting the kit’s ongoing-call component inside your panel may not dock it, because some kits always open it full screen.
  4. Indicator: show that a huddle is active, and who started it, from the latest meeting message. Show who is in it only from the Calls SDK’s participant events; never guess.
  5. Leave: leave the session and release the microphone and camera when the dock closes. Keep the dock mounted while the user switches channels.
Levels: L3–L4. Features the target has that CometChat doesn’t (huddle threads, shared notes, screen drawing) are not natively possible; list them. A call is done only when two different users on two browsers or devices connect and hear (and see) each other.

Auth and session

A production app has a session, not a hardcoded user. Build this even for a demo:
  • Gate the app on auth state. While the SDK initializes, show a splash/skeleton; if no user is logged in, show a login screen; once logged in, show the app; on logout, return to login. Never drop straight into a single baked-in UID.
  • Login screen. At minimum a field/flow to sign in the app’s user (your own auth, SSO, or a user picker for a demo). Persist the session (the SDK stores it) so a refresh stays logged in.
  • Production auth = server-minted tokens. In production, log in with a token your server mints; the dev Auth Key is for local only and must not ship in a public build. The call differs by kit: CometChatUIKit.loginWithAuthToken(token) on React, Angular, Android and Flutter; CometChatUIKit.login(authToken:) on iOS; CometChatUIKit.login({ authToken }) on React Native.
  • Logout clears the CometChat session and returns to the login screen.
  • Error/edge states. Wrong credentials, network failure, or an expired session each show a clear message and a retry — never a blank screen or an infinite spinner.

Empty app and sample data

A new app often has no conversations yet, so a fresh clone can look dead. That is a correct state, not something to fix behind the user’s back:
  • Never send messages, reactions, or calls, or create users and groups, on anyone’s behalf without the user’s yes — not in the app’s code, a startup “seed” script, or through the API. Messages sent as real users land in their real conversations.
  • Say it and ask. Let the kit’s empty states show, tell the user the app has no conversations yet, and offer sample data, saying exactly what you’d create: which app, how many users and groups, and how many messages between whom.
  • Only after a yes: create it in a development app, outside the app’s runtime code (account tools for users and groups; a one-off script with a server-side REST API key for messages), mark it as sample data, and list what was created and how to remove it.
Note: A demo that logs in as a fixed UID with no login UI, no logout, and the Auth Key in the client bundle is a prototype, not production. Ship the gate + a login screen, and use auth tokens for anything real.
A real messaging app is many screens, not one list + one message pane. Wire all of these and make every affordance open a real screen (no dead buttons). The table says what must exist; where and how each one opens comes from the target (see Cloning an existing app) or, with no target, from the archetype’s interaction patterns: Placement follows the target. If the target opens search in a centered dialog, a channel directory in a dialog, a thread in a side panel, or settings on a separate page, do exactly that — host the kit component in that container (L3) rather than accepting the kit’s default placement. The defaults below apply only when there is no target and the archetype doesn’t say otherwise. Mobile routing (this is where basic clones break): use a router or an explicit screen state so Search, Details, New Chat, Calls, and Settings each open as their own full screen with a back button (or the target’s mobile pattern, such as a bottom sheet) — not an overlay squeezed into a 2-pane desktop layout that silently does nothing on a phone. Default on desktop: details/search open as the third pane or a drawer; on mobile they push over the chat. If tapping a control on a phone does nothing, the screen wasn’t wired — that is a defect to fix, not an acceptable limitation.

UX principles

These are what separate a real messenger from a toy. Every archetype includes them unless noted.
  • Optimistic send. The message appears instantly (pending state), then reconciles to sent → delivered → read. Never block the composer on the network.
  • Receipts & status. Sent/delivered/read ticks (or equivalent), consistent and legible.
  • Typing indicator & presence. Live “typing…” and online/last-seen where the archetype allows.
  • Message grouping. Consecutive messages from the same sender within a short window are grouped (one avatar, tight spacing); a new sender or time gap starts a new group. Date separators and an unread divider (“New messages”) are shown.
  • Smooth scrolling. Auto-scroll to newest when at the bottom; a scroll-to-bottom affordance with an unread count when scrolled up; preserve position when loading older pages.
  • Rich media done right. Thumbnails with aspect ratio and loading/upload progress; tap to expand; audio/voice notes with waveform + scrubber; file cards with type/size; link previews.
  • Reply, react, edit, delete. Quote/reply with a jump-to-original; emoji reactions with a picker and counts; edit/delete with tombstones (“This message was deleted”).
  • Threads where the archetype needs them (team, community, and support apps).
  • Mentions & search. @-mentions with autocomplete; conversation and message search.
  • Actions. Long-press (mobile) / hover (web) message menu; swipe-to-reply on mobile.
  • Empty, loading, error, offline states for every surface — never a blank screen; skeletons while loading; a clear reconnecting state.
  • Notifications. Push when backgrounded; in-app unread badges on conversations and app icon/tab.
  • Accessibility & i18n. Keyboard navigation, focus states, screen-reader labels, dynamic type, full dark mode, and RTL for RTL locales.

Design tokens

Express the design as semantic roles and map each to the family’s real theme keys (web: CSS custom properties; native: the theme object). Keep two complete sets — light and dark — and switch with the OS unless the app offers a manual toggle.

Color roles

The neutral values are generic examples, and every accent is derived from brand, so with no target the app looks like the customer’s brand, not like any existing messenger. Replace brand/primary (and the bubble-out tint) with the customer’s palette. When cloning, replace every role with the value measured from the target (see Design sync). Keep contrast ≥ 4.5:1 for text.

Typography

Spacing, radius, elevation, density

Premium look

Setting only the primary color (--cometchat-primary-color on web, the primary color in the theme object on native) gives a flat, generic result (the “basic” look). A premium, best-in-class finish needs the details. The values below are the default consumer-messenger finish for when there is no reference app; when cloning, the target’s measured look wins over every item here (a team workspace, for example, usually has flat, bubble-less messages and no wallpaper):
  • Chat wallpaper, not a flat color. Real messengers put a subtle patterned wallpaper behind the messages. Set --cometchat-message-list-bg to that wallpaper — a tiled pattern (data-URI or asset) or at least a tint: bg mixed a few percent toward brand (a warmer or cooler off-white in light, a near-black in dark), never plain white.
  • Bubbles with tails, shadow, and tight radii. Message bubbles have a small tail on the sender’s side, a subtle drop shadow (0 1px .5px rgba(0,0,0,.13)), a tight radius (about 8px; use 12–18px if you drop the tail), and comfortable padding. Outgoing uses bubble-out (a brand tint), incoming uses bubble-in. A flat, shadowless, tail-less rounded rectangle is exactly what reads as “basic.”
  • Ticks & meta done right. Timestamp + status ticks sit inside the bubble, bottom-right: single grey → double grey (delivered) → read in the receipt-read accent. Don’t drop or recolor them arbitrarily.
  • Header + list rows. The chat/app header uses the brand color with legible on-brand text; list rows show avatar, name, last-message preview, time, and an unread badge (brand-colored pill).
  • New-chat FAB on mobile, plus hover/press states on rows and buttons — interactive things must look interactive.
  • Density & type. Use the token type scale + spacing; don’t cram or over-space. Match the reference app’s density (comfortable for consumer messengers, compact for team/community apps).
  • Avatars & icons. Circular avatars with initials fallback, a presence dot, and one consistent icon set. Full dark mode with the dark palette — not just inverted text.
The CSS variable names above are the web UI Kit’s; on native, set the same things through the family’s theme and the component style objects (see Per-platform notes). Pull the exact recolorable tokens/classes from the family’s theming and message-bubble styling docs. The point is to spend the polish (wallpaper, tails, shadows, ticks, badges), not stop at the primary color.

Theming contract (where tokens live, and how the cascade resolves)

Overriding the primary color is not the same as owning the theme. On the web UI Kit, three facts decide whether your tokens actually win — get them wrong and the app stays half-branded (this is the single most common “my theme didn’t apply” surprise):
  • Set the brand tokens on BOTH the kit wrapper and the document root. The kit reads most tokens from the .cometchat wrapper, but some values — notably the accent baked into generated icon assets — are read from :root / document.documentElement. Declare the full brand set (primary + the extended primary ramp 50–900 + highlight icon/text/border + button/FAB backgrounds) on both :root (and html) and .cometchat. Tokens set only on .cometchat leave the root-read values on the kit default.
  • Win the cascade on purpose. The kit ships its own rules on .cometchat / [data-theme].
    • React v7 declares them inside @layer cometchat, so any unlayered rule of yours already wins, whatever the stylesheet load order. If your own CSS is layered (for example Tailwind’s @layer), extra specificity won’t help: declare the order with your layer last (@layer cometchat, app;) or put the token overrides outside any layer.
    • Angular ships unlayered CSS, so raise specificity deliberately: the doubled-class .cometchat.cometchat { … } for tokens, and the same-element .cometchat[data-theme="dark"] { … } (not a descendant selector) for the dark set.
    Either way, define every token in the base rule, then redefine only what changes under dark.
  • Some icons are a CSS filter, not a token — so you override the filter. The composer / attachment / emoji icons are <img> assets recolored by a hardcoded CSS filter (brightness(0) … hue-rotate(…deg) …) that no --cometchat-* variable touches. Brand them by overriding that filter on the specific rules that carry it, and solve the filter numerically rather than guessing: draw the icon to a <canvas> with a candidate ctx.filter, read the average pixel, and sweep hue-rotate / invert / saturate to minimise distance to your brand color. Full recipe and selector list in Known UI Kit gotchas.
Treat theming as a contract — where tokens live, plus how specificity resolves — not a single variable. (Native families theme through the platform theme object instead; the “both scopes” and “filter icons” points are web-specific.)

Design sync: kit components and your own UI as one design

A clone usually mixes themed kit components with a few pieces of your own UI. Users must not be able to tell which is which, and both must match the target. Do it in this order:
  1. Measure the target. From screenshots or the real app, sample the colors for every color role in light and dark, and note the font family, type scale, spacing, radii, borders vs shadows, icon style, avatar shape and size, and density. Record them in the requirements file.
  2. One token source. Put the measured values in one place (CSS custom properties on web; a theme object on native) and derive everything from it. Never hard-code a color or size in a component.
  3. Theme the kit from those tokens through the family’s theming API, including the parts that are easy to miss: menus and context menus, dialogs, the emoji keyboard and pickers, reaction chips, date separators, unread dividers, empty and error states, avatars, badges, and the icons recolored by a filter (see Theming contract).
  4. Build your own UI from the same tokens — the same colors, type scale, spacing, radii, icon set, hover and focus states, and motion — so a custom dialog or quick switcher sits next to a kit list without a visible seam.
  5. Use kit slots for in-component differences. If a row, header, or bubble must look different, use the kit’s custom views/templates (L2) styled with the shared tokens, rather than overriding internal classes.
  6. Compare side by side. For each screen and state — light and dark, mobile and desktop — put a screenshot of the target next to a screenshot of the build and list every difference: layout, spacing, color, type, icon, copy, placement, behavior. Fix them and repeat until each remaining difference is either fixed, agreed with the user, or recorded as not reachable (with the reason) in the requirements file.
If the kit can’t reach a visual detail through tokens or slots, move that item up a level or record the gap — don’t fight the kit with fragile overrides.

Motion and animation

Match the real product’s feel. Durations are guidance; respect prefers-reduced-motion (and the platform’s reduce-motion setting) by dropping to instant/opacity-only. On iOS/Android/Flutter, prefer the platform-native transition primitives; on web, prefer transform/opacity (GPU-friendly) over layout-animating properties.

Sending feels smooth (one change, not two)

In a real messenger a sent message appears once, and only its status changes (clock, then ticks). In a clone it often appears, then jumps or re-renders when the server confirms it. The cause: the kit shows a pending copy keyed by its muid, then swaps in the confirmed message with a server id, and the server’s sentAt can differ from the local one. Where the kit keys rows by id, the row (and its date separator) mounts again.
  • Anything stateful in a bubble (an entrance animation, a loaded image, animated emoji, a custom view’s state) must survive that remount. Key your own state by muid (fall back to id), and make custom views render the same result from the same message, with no “first render” effects.
  • An entrance animation runs once per muid, never again on the confirmed copy. Don’t detect new rows by watching the DOM, and never patch kit markup with scripts; change it through the kit’s templates, custom views and props.
  • Check it: send a text, an emoji-only message and an image, and record the last row’s position and size from send until it’s confirmed. Only the status icon may change. If a remount still shows and you can’t hide it through the kit’s API, report it as a known kit limitation with the version.

Layout and responsiveness

Rules: the chat pane always shows a header, a scrollable message list, and a pinned composer. The list column keeps search + new-chat affordances. Never let the body scroll horizontally; media and the message list scroll within their own containers.

Layout correctness

The rules below are for the web UI Kits (React, Angular). Native apps have the same goals — the message list scrolls on its own, the composer stays pinned above the keyboard, nothing hides under the notch or system bars — but reach them through the platform’s own tools; see Per-platform notes. The #1 reason a web clone looks broken — an empty gap under the messages, the whole page scrolling, or the composer floating — is flex/height mistakes. Follow these exactly:
  • Full-viewport root. html, body, #root { height: 100%; margin: 0 }; the app shell uses height: 100dvh (dynamic viewport height — accounts for mobile browser chrome, so there is no empty strip at the bottom). Reset the starter’s #root (max-width: none; padding: 0; width: 100%).
  • The chat column is a flex column that fills its parent and clips: display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden.
  • The message list flexes and scrolls internally: give it flex: 1 1 auto; min-height: 0. The min-height: 0 is exactly what stops the list from pushing the layout taller and leaving an empty gap / making the whole document scroll. Header and composer are flex: 0 0 auto (composer pinned to the bottom).
  • Only the message list scrolls — never the page body. If you see the whole screen scroll, or a gap below the last message, a flex parent is missing min-height: 0 / overflow: hidden, or the root isn’t 100dvh. Fix that before shipping.
  • Keyboard-safe on mobile. Use 100dvh (not 100vh) so the composer stays above the on-screen keyboard; keep the composer sticky. Never put transform or filter on an ancestor — it clips the kit’s fixed overlays (emoji picker, context menus, call screen).

Component map

Use these real UI Kit components (names per family may vary slightly — confirm in the family docs): When a design needs a layout the prebuilt components don’t cover, first check whether hosting the component in your own container or using its custom views reaches it; if not, build the custom UI on the Chat/Calls SDK (headless) — but reuse these tokens, motion, and principles so it still feels consistent. See Kit component or your own UI?.

App archetypes (recipes)

Each recipe = layout + components + the features to enable + token/vibe deltas. All inherit the UX principles. The archetypes below are described by their characteristics, not by any one branded app — pick the closest fit in discovery. If the user names an app to emulate, the archetype is only the starting point: the researched target (Cloning an existing app) overrides every layout, interaction, and visual below. Each archetype lists its usual interaction patterns — where things open and how they’re reached — so an app built without a reference still behaves like its category, not like the kit’s default layout.

Consumer 1:1 + group messenger (default)

  • Layout: mobile single-pane / desktop 3-pane. Chat list with avatars, last message, time, unread.
  • Components: Conversations, MessageHeader (+CallButtons), MessageList (+bubbles), MessageComposer, Reactions, a thread panel (optional: ThreadHeader + MessageList + MessageComposer), Groups/GroupMembers, IncomingCall/OutgoingCall.
  • Features: 1:1 + groups, media + docs, voice notes, reactions, reply, delivery/read ticks, typing, presence/last-seen, voice/video calls, push, disappearing messages (optional), starred/saved.
  • Vibe: rounded bubbles, outgoing tinted, read-tick accent, comfortable density.
  • Interaction patterns: the chat list is home; search sits at the top of the list and filters it in place, then shows message results below; new chat from a floating button or header icon opens a contact picker (full screen on mobile); long-press a message → reaction bar above + action menu; swipe to reply; tap the chat header → contact/group info as its own screen with shared media; in-chat search from the header menu with up/down result navigation.

Large-community / channels messenger

  • Large groups/channels, message folders/filters, fast search, stickers/GIFs, reactions, silent send, edit history, threads. Denser than the consumer default; snappy motion.
  • Interaction patterns: folder tabs above the list; global search covers chats, public channels, and messages in one results view with sections; channel info shows subscriber count and a join button for non-members; right-click / long-press context menus on chats and messages.

Minimal personal messenger

  • Minimal, high-contrast bubbles with tails, bubble-anchored quick reactions, inline media, typing bubble. 1:1 focus + small groups. Comfortable density; understated motion.
  • Interaction patterns: double-tap or long-press a bubble for quick reactions anchored to it; details from the header; few visible controls, most actions behind long-press.

Team workspace (channels + DMs)

  • Workspaces → channels + DMs; threads-first; rich composer (formatting, code, attachments); mentions; left sidebar (channels/DMs), desktop 3-pane, compact density.
  • Interaction patterns: a quick switcher opened by a keyboard shortcut (Ctrl/Cmd+K) in a centered dialog to jump to any channel or person; global search in a centered dialog or a top-bar field with results grouped by type (messages, channels, people, files) and filters; a channel directory (“browse channels”) dialog listing public channels with member counts, preview, and join; collapsible sidebar sections (starred, channels, direct messages) with bold unread rows and mention badges; threads in a right side panel with the channel still visible; a hover toolbar on messages (react, reply in thread, more); flat messages (avatar + name + time, no bubbles) grouped by sender; channel header with name, topic, and member count opening a details dialog; Up arrow to edit your last message; Enter sends, Shift+Enter adds a line; avatar and name on every message run, including your own and in DMs; huddles (a live audio session in a channel that others see and join, without ringing) docked at the bottom of the sidebar and expandable into a window, built with the huddle steps. On mobile see Mobile equivalents.

Community server (voice + text)

  • Servers → channels; strong presence; voice channels; dense, dark-first; roles/mentions; reactions; inline media.
  • Interaction patterns: a server rail on the far left, channel categories that collapse, a member list on the right grouped by role and online state, role-colored names, hover toolbar on messages, user profile popover on avatar/name click, a quick switcher by keyboard shortcut. On mobile see Mobile equivalents.

Support / live-chat

  • Embedded widget or full-screen; 1:1 user↔agent; quick replies, canned responses, bot→agent handoff, transcript, post-chat rating. Minimal chrome; brand-forward.
  • Interaction patterns: a launcher button in a corner opens the chat as a floating panel (full screen on mobile); optional pre-chat form; quick-reply chips above the composer; “agent is typing” and estimated wait; end-chat confirmation and rating in the same panel.

Community / social

  • Feeds + DMs; large public groups; moderation (flag/ban), roles; discovery/search.
  • Interaction patterns: DMs reached from an inbox icon in the app’s main navigation; a group directory with search and join; report and block from message and profile menus.

Marketplace / dating

  • 1:1 with media, safety (block/report/moderation), read receipts optional, presence optional; clean, trust-forward.
  • Interaction patterns: conversations start from a listing or profile (“Message” button), so the chat header shows that context (item card or profile); safety actions (block, report) are one tap from the header; the inbox may filter by listing or match.

Full feature checklist

“Full feature” means the agent has enabled all of these that apply to the archetype (some are props, some are Dashboard settings — verify each in the docs):
  • Messaging core: 1:1, groups, send/edit/delete, reply/quote, optimistic send, receipts, typing, presence, grouping, date separators, unread divider.
  • Rich media: images, video, audio, files, voice notes, stickers/emoji/GIF, link previews, thumbnails + progress, media viewer.
  • Engagement: reactions, mentions, polls, pinned messages, saved/starred, threads.
  • Groups & moderation: create/manage groups, roles/scopes, add/remove/ban, report/flag.
  • Calls: 1:1 and group voice/video, incoming/outgoing UI, call logs. Calling is off until the family’s Calls SDK is installed and calling is enabled at init — a themed UI with call buttons that do nothing is not calling. Group calls are joined, not rung, so drop-in rooms (“huddles”) are group calls plus your own presence indicator.
  • Discovery: conversation + message search, new-chat/user picker.
  • Notifications: push (FCM/APNs), in-app badges, mute/preferences.
  • Personalization: dark mode, density, per-conversation settings.
  • Accessibility & i18n: keyboard, screen-reader, dynamic type, RTL, localization.
  • Security/production: server-minted auth tokens, blocked users, data handling.

Building a fresh app (end-to-end)

  1. Discover & agree scope — run Discovery and requirements: ask the required questions (incl. the third-party-package policy), research the feature set (for a clone, the full target inventory), pick a build level for every item, write cometchat-requirements.md, and get the checklist approved before writing code.
  2. Install the CometChat UI Kit for the target framework; wire init → login (dev auth key for local, server-minted auth tokens for production).
  3. Apply tokens: set the light + dark token sets via the family theming API — measured from the target when cloning (Design sync); drop in the customer’s brand + logo.
  4. Compose the layout for the form factor and archetype using the component map, with every feature opening in the target’s container (dialog, popover, side panel, page).
  5. Enable features from the approved checklist, one item at a time (props + Dashboard settings), at the agreed build level, comparing each against the target before moving on.
  6. Wire calls & push if the archetype includes them.
  7. Add motion & responsiveness per the specs above (or the target’s).
  8. Verify with the acceptance checklist and, for a clone, the side-by-side comparison; fix; ship.

Retrofitting an existing app (non-breaking)

  1. Branch; keep a clean tree. Never force-delete non-chat code.
  2. Inventory the app’s current chat/UI, routing, state, and auth.
  3. Map each existing chat surface to a CometChat component (or headless SDK), choosing a build level that keeps the app’s current behavior: where each feature opens, its shortcuts, and its states. The existing app is the target.
  4. Layer the theme: apply the token roles onto the app’s existing brand so the kit matches the app, not the reverse.
  5. Swap region-by-region behind the existing routes and state — list, then chat pane, then calls — verifying the build after each. Preserve auth and navigation.
  6. Remove only the UI you replaced; leave everything else intact.
  7. Verify parity with the checklist; confirm the build passes and there are no console errors.

Known UI Kit gotchas

Verified traps a generic design doc won’t warn you about — each has cost real build time. Check them explicitly. The items below are web UI Kit (React v7, and Angular where it shares the CSS-variable theming); component and prop names are React’s. Native gotchas are in Per-platform notes.
  • Off-brand icons that no token fixes (web). The composer / attachment / emoji icons are <img> data-URIs recolored by a hardcoded CSS filter (… hue-rotate(235deg) … = the kit’s default accent), not a --cometchat-* token — so brand tokens leave them the default color, most visibly in dark mode. Idle composer icons use a neutral grey filter; the accent filter applies on hover and active states. Fix: override the filter on the rules that carry the accent. Selectors (React UI Kit v7.2), each followed by .cometchat-message-composer__button-icon unless noted: .cometchat-message-composer__attachment-button:hover, __emoji-button:hover, __sticker-button:hover, __voice-button:hover; .cometchat-message-composer__attachment-button--active, __emoji-button--active, __sticker-button--active; .cometchat-message-composer__ai-button:hover, __ai-button--active; .cometchat-message-composer__attachment-option-icon (on its own, no descendant); .cometchat-emoji-keyboard__tab--active .cometchat-emoji-keyboard__tab-icon. Leave the --disabled:hover rules (they reset to grey) and __voice-button--active (red while recording) alone. Solve the filter for your brand color by canvas-sampling (see Theming contract). Worked example (a teal; solve your own brand the same way): brightness(0) saturate(1) invert(.4) sepia(.6) saturate(6) hue-rotate(120deg) brightness(.85) contrast(1) (measured rgb ≈ 1,125,104).
  • Search needs the Search feature enabled on the app’s plan. CometChatSearch (conversation + message search) returns an error (HTTP 402) until Search is enabled on the app’s plan (dashboard), and the component itself shows no message about it. Correctly wired search still returns nothing until then. Fix: always pass onError and surface the gate to the user instead of a dead search box, and tell the customer to enable Search on their plan. Never assume “search works” because the component rendered.
  • Message search is a component. On React, CometChatMessageHeader already shows a search button (showSearchOption, on by default); handle onSearchOptionClicked to open search, or set showSearchOption={false} if you add your own trigger, so the header doesn’t show two. The other kits’ headers have no search button. Either way, use CometChatSearch and open it from wherever the target opens search: the conversation list’s onSearchBarClicked and a header auxiliaryButtonView trigger are the kit’s default entry points, but you can equally render CometChatSearch inside your own centered dialog, popover, or side panel opened by a top-bar field or a keyboard shortcut. Route results with onConversationClicked / onMessageClicked → CometChatMessageList’s goToMessageId. If the target groups results in a way the component doesn’t render, build that view on the SDK instead (L4). New chat uses CometChatUsers. Confirm exact component/prop names in the family docs before wiring.
  • Give the details panel an explicit way in. Tapping the avatar or name (CometChatMessageHeader’s onItemClick) is easy to miss, so don’t make it the only way in — add an explicit info button (e.g. in auxiliaryButtonView) so the details panel is always reachable. A details screen you can’t open is a dead control.
  • Verify in DARK mode, not just light. Several of the above (especially the icon filter) look fine in light and regress in dark. Test the composer, attachment menu, emoji keyboard, and reactions with the theme set to dark before calling it done.
  • Don’t put transform / filter on an ancestor of the kit. It clips the kit’s fixed overlays (emoji picker, context menus, call screen). Keep those off the app shell.

Per-platform notes

Everything above the token values is shared. This section covers what differs per platform: how the theme is applied, how layout and navigation work, and the traps we’ve hit. Exact class, prop, and method names still come from each family’s docs — the names here are pointers, current at the time of writing; confirm them before use.

Theming per platform

On native there is no CSS cascade and no icon filter: the kit’s icons are tinted through the theme and component styles. If the theme doesn’t reach an icon, pass your own icon through the component’s icon or style property, if the family docs list one.

Layout and navigation per platform

On every platform: the message list scrolls on its own, the composer stays visible with the keyboard open, nothing sits under the notch, status bar, or home indicator, and back (gesture, hardware button, or Esc on web) closes the top-most screen, sheet, or dialog first.

Mobile equivalents of desktop patterns

When a target is desktop-first, its mobile version (or the platform convention, if it has none) decides how each pattern works on a phone. Research the target’s mobile app if it exists; otherwise use these:

Localization per platform

The API differs by family — don’t assume one name everywhere: React, Angular, iOS, and Android use CometChatLocalize; React Native uses CometChatI18nProvider with the useCometChatTranslation hook; Flutter uses CometChatLocalize too, plus the kit’s Translations.delegate in localizationsDelegates (check the version’s docs — this has been changing). Set the language, then check right-to-left layouts in an RTL locale.

Native gotchas

  • React Native with Expo needs a development build. Expo Go isn’t supported because the UI Kit uses native modules; build a dev client. Calls and push also need native setup, so start the native build early rather than at the end.
  • React Native: install every peer dependency the integration guide lists (for example react-native-gesture-handler, react-native-svg and react-native-video) — a missing one fails at runtime, not at install.
  • iOS: theme before init. Theme values set after CometChatUIKit.init() don’t reach components that are already created.
  • Flutter: theme both modes, and merge extensions. A palette registered only on the light ThemeData leaves dark mode on kit defaults; replacing extensions drops the ones an ancestor set.
  • Android: day/night resources. Brand colors set only in values/themes.xml stay unbranded in dark mode unless values-night overrides them too.
  • All native platforms: test on a real device or emulator for keyboard, safe-area, permission (camera, microphone, notifications, photos), and push behavior — simulators and previews miss them.
  • Verify in dark mode and at large text sizes (Dynamic Type, font scale): fixed heights in custom rows and headers clip text.

Verify it looks and feels real

Ship only when all pass:
  • Login works: logged-out shows a login screen; logging in enters the app; logout returns to login; refresh stays signed in. No hardcoded UID, no Auth Key in a public production build.
  • No layout bugs: the message list fills the space and scrolls internally; the composer is pinned; no empty gap under the messages and no whole-page/over-scroll; correct with the mobile keyboard open (100dvh).
  • Every control works on mobile: tapping search filters; the chat header opens contact/group details (profile, shared media, members); the FAB opens new chat; the call button starts a call; settings is reachable. No dead buttons.
  • Premium look, not flat: when there’s no target, patterned/tinted chat wallpaper; bubbles have tails + subtle shadow; ticks grey, then the receipt-read accent on read; unread badges; avatars + presence; proper density — it doesn’t look like the default demo. When cloning, the target’s look replaces these.
  • Clone parity (when the user named an app): every item in the requirements table is built at its agreed level or explicitly left out; every feature opens in the same place, through the same entry points and shortcuts, and closes the same way as in the target; side-by-side screenshots of each screen and state (light + dark, mobile + desktop) show no unagreed difference; kit components and your own UI share the same tokens with no visible seam; no unverified detail was shipped as a guess.
  • Message details match: open a 1:1 chat and a group and compare your own and others’ messages with the target: avatar, name, alignment, grouping, timestamps, receipts.
  • Live calls show in the target’s container: docked, mini window or full screen, as recorded.
  • Secondary surfaces match: open each one (thread, message info, reactions list, pickers, menus, dialogs, viewer, call screens) and compare it with the target; none is left in the kit’s default look.
  • Motion actually moves: for every motion row, two frames about 300 ms apart differ; a static fallback counts as not done.
  • Sending is one visual change: a sent message appears once and only its status icon changes (see Sending feels smooth).
  • Bubbles grouped correctly; date separators + unread divider present.
  • Optimistic send; sent/delivered/read states; typing; presence.
  • Smooth auto-scroll + scroll-to-bottom with unread count.
  • Media thumbnails + progress; voice notes; link previews.
  • Reactions, reply/quote (with jump-to-original), edit/delete tombstones.
  • Search returns results or an honest plan-gated state (never a silent dead box — Search can 402); mentions work; long-press/hover menu; swipe-to-reply (mobile).
  • Empty / loading (skeleton) / error / offline states on every surface.
  • Responsive at mobile / tablet / desktop; no horizontal body scroll; safe areas + keyboard-avoidance.
  • Native: themed in light and dark through the platform’s theme API (not per-screen hacks); back gesture / hardware back closes the top-most sheet or screen; desktop patterns use their mobile equivalents; checked on a real device or emulator.
  • Full dark mode — including on-brand icons in the composer / attachment menu / emoji keyboard (these are recolored by a hardcoded filter, not a token; see Known UI Kit gotchas); RTL for RTL locales; prefers-reduced-motion respected.
  • Customer brand + logo applied; no CometChat branding as the app identity.
  • Push + unread badges; calls (if in scope) ring and connect between two different users on two browsers or devices, with audio (and video) both ways. If you couldn’t run that check, report calling as built but not verified, never as working.
  • No duplicate requests: opening the app, opening a conversation, and sending a message each make every request once in the network log (twice only under React’s development StrictMode, with the guards making the second a no-op).
  • No data created without asking: no messages, reactions, calls, users, or groups were created on anyone’s behalf unless the user said yes; any sample data is listed with how to remove it.
  • Build passes; no console errors; APIs used all exist in the docs.

Brand ownership

The app’s identity is the customer’s — their name, logo, colors, and tone. CometChat is the messaging engine underneath. Replace the brand/primary tokens, the bubble-out tint, and the logo/ app name; keep the component structure, principles, motion, and layout. Do not present CometChat’s own branding as the product’s.

Where to get exact APIs

This blueprint is the what and how it feels; get the exact how from the live docs: Never answer a prop or method from memory — fetch it from the pages above.