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.
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.
- 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.
- 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?.
- 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.
- 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.”
- 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.
- 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.
- Enable the features from the full feature checklist — some are props, some are Dashboard settings. Implement the archetype’s whole set, not a subset.
- Apply motion (Motion and animation) so send/receive/typing/reactions/ calls feel native — and, when cloning, like the target.
- Keep the customer’s brand — replace brand tokens + assets only; never ship CometChat branding as the app’s identity (Brand ownership).
- 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
.mdtwins. 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 incometchat-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:
- 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.) - 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.
- 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.
- 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 acometchat-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:
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
- 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.
- 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.
- The target’s public help center and docs, which describe features, shortcuts, and settings precisely.
- 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.
Legal and brand boundary
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.
- 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.
- Join: replace the kit’s call bubble so Join opens your container, not the kit’s full-screen call screen.
- 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.
- 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.
- Leave: leave the session and release the microphone and camera when the dock closes. Keep the dock mounted while the user switches channels.
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.
Navigation and screens
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 frombrand, so with no target the app looks like the customer’s brand, not like any existing messenger. Replacebrand/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-bgto that wallpaper — a tiled pattern (data-URI or asset) or at least a tint:bgmixed a few percent towardbrand(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 (about8px; use12–18pxif you drop the tail), and comfortable padding. Outgoing usesbubble-out(abrandtint), incoming usesbubble-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-readaccent. 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.
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
.cometchatwrapper, 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 ramp50–900+ highlight icon/text/border + button/FAB backgrounds) on both:root(andhtml) and.cometchat. Tokens set only on.cometchatleave 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.
- React v7 declares them inside
-
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 CSSfilter(brightness(0) … hue-rotate(…deg) …) that no--cometchat-*variable touches. Brand them by overriding thatfilteron the specific rules that carry it, and solve the filter numerically rather than guessing: draw the icon to a<canvas>with a candidatectx.filter, read the average pixel, and sweephue-rotate/invert/saturateto minimise distance to your brand color. Full recipe and selector list in Known UI Kit gotchas.
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:- 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.
- 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.
- 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).
- 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.
- 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.
- 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.
Motion and animation
Match the real product’s feel. Durations are guidance; respectprefers-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 itsmuid, 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 toid), 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 usesheight: 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. Themin-height: 0is exactly what stops the list from pushing the layout taller and leaving an empty gap / making the whole document scroll. Header and composer areflex: 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’t100dvh. Fix that before shipping. - Keyboard-safe on mobile. Use
100dvh(not100vh) so the composer stays above the on-screen keyboard; keep the composer sticky. Never puttransformorfilteron 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)
- 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. - Install the CometChat UI Kit for the target framework; wire
init → login(dev auth key for local, server-minted auth tokens for production). - 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.
- 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).
- 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.
- Wire calls & push if the archetype includes them.
- Add motion & responsiveness per the specs above (or the target’s).
- Verify with the acceptance checklist and, for a clone, the side-by-side comparison; fix; ship.
Retrofitting an existing app (non-breaking)
- Branch; keep a clean tree. Never force-delete non-chat code.
- Inventory the app’s current chat/UI, routing, state, and auth.
- 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.
- Layer the theme: apply the token roles onto the app’s existing brand so the kit matches the app, not the reverse.
- 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.
- Remove only the UI you replaced; leave everything else intact.
- 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 CSSfilter(… 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 thefilteron the rules that carry the accent. Selectors (React UI Kit v7.2), each followed by.cometchat-message-composer__button-iconunless 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:hoverrules (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 passonErrorand 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,
CometChatMessageHeaderalready shows a search button (showSearchOption, on by default); handleonSearchOptionClickedto open search, or setshowSearchOption={false}if you add your own trigger, so the header doesn’t show two. The other kits’ headers have no search button. Either way, useCometChatSearchand open it from wherever the target opens search: the conversation list’sonSearchBarClickedand a headerauxiliaryButtonViewtrigger are the kit’s default entry points, but you can equally renderCometChatSearchinside your own centered dialog, popover, or side panel opened by a top-bar field or a keyboard shortcut. Route results withonConversationClicked/onMessageClicked→CometChatMessageList’sgoToMessageId. If the target groups results in a way the component doesn’t render, build that view on the SDK instead (L4). New chat usesCometChatUsers. 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’sonItemClick) is easy to miss, so don’t make it the only way in — add an explicit info button (e.g. inauxiliaryButtonView) 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/filteron 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 useCometChatLocalize; 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-svgandreact-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
ThemeDataleaves dark mode on kit defaults; replacingextensionsdrops the ones an ancestor set. - Android: day/night resources. Brand colors set only in
values/themes.xmlstay unbranded in dark mode unlessvalues-nightoverrides 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-readaccent 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-motionrespected. - 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 thebrand/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:- Per-product LLM indexes — the scoped routing indexes for each UI Kit / SDK (append
.mdto any docs URL for its clean twin), and the site-wide llms.txt. - Theming — your family’s theming guide for the real theme keys these token roles map to: React, Angular, React Native, iOS, Android, Flutter.
- Localization — React, Angular, React Native, iOS, Android, Flutter.
- Components — the component’s page in your family for verified props and defaults — e.g. Conversations. Component names are consistent across families; confirm the exact props per family.
- Skills & MCP — Agent Skills to install the CometChat skills, and the Docs MCP for live docs inside the agent.
- Key concepts — Fundamentals: Key Concepts.