with one click
cometchat-native-bare-patterns
// Integration patterns for bare React Native CLI projects — pod install, Info.plist + AndroidManifest permissions, Apple privacy manifest, native module linking, Metro config.
// Integration patterns for bare React Native CLI projects — pod install, Info.plist + AndroidManifest permissions, Apple privacy manifest, native module linking, Metro config.
Component catalog for the CometChat React Native UI Kit v5 — names, props, slot views, request builders, hide flags, style shape. Always loaded before writing CometChat* JSX.
Shared rules for CometChat React Native UI Kit v5. Always loaded alongside framework (expo/bare) and placement skills. Read this first.
Customize the CometChat React Native UI Kit without forking — four-tier model: props → request builders → text formatters + message templates → DataSource decorators + event bus.
Integration patterns for Expo managed workflow — app.json config, permissions, gesture handler setup, env vars, Expo Router file-based routing subsection.
Feature catalog for React Native — calls (separate SDK + WebRTC), extensions (polls / stickers / translation / link preview / collaborative doc / whiteboard / smart replies), AI agent, in-call chat. When to toggle, install, or swap.
Where to put chat in a React Native app — Stack screen, BottomTab, Modal, BottomSheet, Embedded. Maps each to CometChat component composition with ASCII layout references.
| name | cometchat-native-bare-patterns |
| description | Integration patterns for bare React Native CLI projects — pod install, Info.plist + AndroidManifest permissions, Apple privacy manifest, native module linking, Metro config. |
| license | MIT |
| compatibility | Node.js >=18; React Native >=0.77; @cometchat/chat-uikit-react-native ^5 |
| allowed-tools | executeBash, readFile, fileSearch, listDirectory, AskUserQuestion |
| metadata | {"author":"CometChat","version":"3.0.0","tags":"cometchat react-native bare cli pods native-modules privacy-manifest"} |
Teaches Claude how to integrate CometChat into a bare React Native CLI project. Covers:
pod install cadence for iOSios/<AppName>/Info.plist for iOS permissionsandroid/app/src/main/AndroidManifest.xml for Android permissionsios/<AppName>/PrivacyInfo.xcprivacy) — required for App Store complianceindex.js + App.tsx with the provider chainRead cometchat-native-core first (init/login/wrapper chain + anti-patterns), then cometchat-native-components, then cometchat-native-placement.
Ground truth: docs/ui-kit/react-native/react-native-cli-integration.mdx, apple-privacy-manifest-guide.mdx, react-native-conversation.mdx + react-native-one-to-one-chat.mdx + react-native-tab-based-chat.mdx, and examples/SampleApp/.
ios/ and android/ folders at the rootpackage.json main is index.js (classic RN entry)expo in package.json dependenciesDo NOT use this skill when:
expo in dependencies + app.json/app.config.js → use cometchat-native-expo-patternsios/ + android/ folders → this can go either way. If the user's workflow is "I edit app.json and run expo prebuild", stay on expo-patterns. If they've fully committed to bare (deleted app.json, edit Info.plist directly), this skill applies.brew install cocoapods on macOS)react-native-cli or @react-native-community/cli usable via npx# Core SDK + UI Kit
npm install @cometchat/chat-sdk-react-native
npm install @cometchat/chat-uikit-react-native
# Required peer deps (natively linked)
npm install \
@react-native-async-storage/async-storage \
@react-native-clipboard/clipboard \
@react-native-community/datetimepicker \
react-native-gesture-handler \
react-native-localize \
react-native-safe-area-context \
react-native-svg \
react-native-video
# dayjs + punycode — no native code but required
npm install dayjs punycode
Bare RN uses autolinking, so no react-native link step is needed. Just confirm everything installed cleanly — if npm install errored mid-way, native modules won't be wired up correctly.
Only if the user's flow includes voice / video calls:
npm install \
@cometchat/calls-sdk-react-native \
@react-native-community/netinfo \
react-native-background-timer \
react-native-callstats \
react-native-webrtc
WebRTC bloats the binary. Skip until the user actually wants calls.
After every npm install of a native module (including the initial install above), run:
cd ios && pod install && cd ..
Without this, Xcode will fail to build with "module not found" errors for native classes. The warning signs:
No such module 'RNGestureHandler' during buildUndefined symbol: _OBJC_CLASS_$_RNCAsyncStorage during linkingIf pod install fails, see cometchat-native-troubleshooting § iOS pod install failures.
Open ios/<AppName>/Info.plist and add:
<key>NSCameraUsageDescription</key>
<string>Allow camera access to send photos and make video calls</string>
<key>NSMicrophoneUsageDescription</key>
<string>Allow microphone access to send voice messages and make calls</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Allow photo library access to send photos</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Allow saving photos from chat to your library</string>
Permission-string best practice: the Usage strings show in the system prompt when iOS asks the user for permission — write them as user-facing copy, not developer notes. "Camera access for video calls" is fine; "for media upload" isn't a real reason a user would accept.
Merge, don't replace. The user may have existing permission strings for other libraries — add only what's missing, don't wipe the file.
PrivacyInfo.xcprivacyRequired for App Store submission since iOS SDK 17 / Xcode 15. If it's missing or incomplete, App Store Connect rejects the upload.
Create ios/<AppName>/PrivacyInfo.xcprivacy with this exact content:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>E174.1</string>
</array>
</dict>
</array>
<key>NSPrivacyCollectedDataTypes</key>
<array/>
<key>NSPrivacyTracking</key>
<false/>
</dict>
</plist>
These 4 reason codes cover the native APIs React Native itself + the UI Kit's react-native-video dependency use:
| API category | Reason code | What it's for |
|---|---|---|
NSPrivacyAccessedAPICategoryFileTimestamp | C617.1 | File-modified timestamps (RN bundler) |
NSPrivacyAccessedAPICategoryUserDefaults | CA92.1 | AsyncStorage (UserDefaults backend on iOS) |
NSPrivacyAccessedAPICategorySystemBootTime | 35F9.1 | Uptime for scheduling (RN + video cache) |
NSPrivacyAccessedAPICategoryDiskSpace | E174.1 | Free-space check (media upload guard) |
After adding:
ios/<AppName>.xcworkspace in XcodePrivacyInfo.xcprivacy — make sure "Add to targets: <AppName>" is checkedIf the user already has a PrivacyInfo.xcprivacy, merge the 4 API types into their existing NSPrivacyAccessedAPITypes array — don't replace the whole file.
cd ios && pod install && cd ..
Open android/app/src/main/AndroidManifest.xml and add inside <manifest> (before <application>):
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- Android 12 and below -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- Android 13+ (API 33+) -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
Merge, don't replace. Keep the user's existing permissions for other libraries.
@react-native-async-storage/async-storage v3+ ships a local Maven artifact that autolinking can't find by default. Without this fix, ./gradlew assembleDebug fails with:
Could not find :react-native-async-storage_async-storage: on any of the paths.
Add the local Maven repo to android/build.gradle:
allprojects {
repositories {
google()
mavenCentral()
// Required for @react-native-async-storage/async-storage v3+
maven {
url = uri(project(":react-native-async-storage_async-storage").file("local_repo"))
}
}
}
Without this fix, the whole Android build fails early. This is a UI Kit-specific gotcha because the kit pins async-storage v3+.
If the project uses custom icon fonts or bundled assets, confirm react-native.config.js includes:
module.exports = {
assets: ["./src/assets/fonts/"], // only if the user has custom fonts
};
Run npx react-native-asset to link. Not required for the UI Kit itself — only relevant if the user extends with custom icons.
index.js + App.tsx with the provider chainindex.js — gesture handler FIRST// index.js
import "react-native-gesture-handler"; // MUST be the first import
import { AppRegistry } from "react-native";
import App from "./App";
import { name as appName } from "./app.json";
AppRegistry.registerComponent(appName, () => App);
The react-native-gesture-handler import must be line 1. Not line 2. Not after React. Without it, swipe gestures on the composer and bottom sheets silently break — often only in release builds, which makes it hard to catch during development.
App.tsx — provider wrapper chain// App.tsx
import React from "react";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { CometChatThemeProvider } from "@cometchat/chat-uikit-react-native";
import { CometChatProvider } from "./src/providers/CometChatProvider";
import { AppNavigator } from "./src/navigation/AppNavigator";
import Config from "react-native-config"; // or read from an env source
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
<SafeAreaProvider>
<CometChatThemeProvider>
<CometChatProvider
appId={Config.COMETCHAT_APP_ID!}
region={Config.COMETCHAT_REGION!}
authKey={Config.COMETCHAT_AUTH_KEY!}
uid="cometchat-uid-1" // dev mode only
>
<AppNavigator />
</CometChatProvider>
</CometChatThemeProvider>
</SafeAreaProvider>
</GestureHandlerRootView>
);
}
The CometChatProvider itself lives in src/providers/CometChatProvider.tsx per cometchat-native-core § 6 — reuse that implementation.
Bare RN has no built-in env var system. Pick one:
react-native-config (most common)npm install react-native-config
cd ios && pod install && cd ..
Create .env at project root:
COMETCHAT_APP_ID=your_app_id
COMETCHAT_REGION=us
COMETCHAT_AUTH_KEY=your_auth_key
Read via:
import Config from "react-native-config";
const appId = Config.COMETCHAT_APP_ID;
iOS post-setup: Xcode needs to know about the .env file. Either use react-native-config's Xcode build-phase script (documented in its README) or create a .xcconfig file. Without this, Config.* returns undefined on iOS.
babel-plugin-dotenv-importSimpler but less mature. Only if the user's already picked this.
For a quick dev-mode proof-of-concept only, just declare the values as constants in a dedicated config file:
// src/config/cometchat.ts
export const CONFIG = {
APP_ID: "YOUR_APP_ID",
REGION: "us",
AUTH_KEY: "YOUR_AUTH_KEY",
};
Add src/config/cometchat.ts to .gitignore. Obviously do not commit real credentials.
REST_API_KEY in the clientThe REST API key is server-only. Production user management + auth token minting happens on a backend you control — see cometchat-native-production.
# iOS
npx react-native run-ios
# Android
npx react-native run-android
First build takes several minutes. Subsequent runs with the Metro bundler already running are fast.
npx tsc --noEmit — TypeScript checkIf any of these fail, see cometchat-native-troubleshooting.
| Change | Command |
|---|---|
| JSX / TS changes | Fast Refresh handles it; r in Metro to reload |
| Added / removed a dep without native code | Restart Metro |
| Added / removed a native module | cd ios && pod install && cd .. then run-ios |
Changed Info.plist | run-ios (Xcode picks up changes) |
Changed AndroidManifest.xml | run-android |
Changed android/build.gradle | ./gradlew clean then run-android |
pod install is mandatory after every native dep change. Missing this → build fails with "module not found" or crashes at runtime with "TurboModuleRegistry could not find X".
import "react-native-gesture-handler" is the FIRST line of index.js. Before React. Before any other import. Missing → swipe gestures silently break (often only in release builds).
Android requires the local Maven repo entry for @react-native-async-storage/async-storage v3+ (see § 3b). Without it, the Android build fails at the assembly step.
The Apple Privacy Manifest is required for App Store submission. Skip it and your app gets rejected. Either create PrivacyInfo.xcprivacy (§ 2c) or merge the 4 API types into an existing one.
Merge permissions into Info.plist and AndroidManifest.xml, don't replace. Wiping out the user's existing permissions breaks their other features.
The four-wrapper chain is at the app root (App.tsx), not per-screen. Re-wrapping per screen causes duplicate init + login, dropped WebSockets, and a 2-3 second flicker on first mount.
REST_API_KEY is never in the client. Use an external backend for token minting — see cometchat-native-production.
Every <CometChatMessageList> must include hideReplyInThreadOption unless you're wiring a full thread panel (see cometchat-native-placement § Hard rule 5).
Q: "TurboModuleRegistry.getEnforcing(...): 'RNAsyncStorage' could not be found"
Forgot pod install. Run cd ios && pod install && cd .. then rebuild.
Q: Android build fails with "Could not find :react-native-async-storage_async-storage:"
See § 3b — add the local Maven repo entry to android/build.gradle.
Q: App Store rejected the build with "ITMS-91053: Missing API declaration" Apple Privacy Manifest is incomplete. Use the full 4-API-type declaration in § 2c.
Q: Composer swipe gestures don't work in production
import "react-native-gesture-handler" isn't the first line of index.js. Move it above all other imports.
Q: CometChatUIKit.login({uid: "..."}) resolves but components don't render
You're probably rendering before init() completes. Gate rendering on CometChatProvider's isReady state — see cometchat-native-core § 6.
Q: I want to support Expo AND bare in the same codebase
Use Expo + npx expo prebuild to generate native projects on demand. Stay on cometchat-native-expo-patterns.
| Skill | When to route |
|---|---|
cometchat-native-core | Init / login / wrapper chain / anti-patterns |
cometchat-native-components | Component prop reference |
cometchat-native-placement | Where chat goes (stack / tabs / modal / bottom sheet / embedded) |
cometchat-native-expo-patterns | Expo managed workflow |
cometchat-native-bare-patterns | This skill — bare RN CLI |
cometchat-native-features | Calls, extensions, AI |
cometchat-native-theming | Theme customization |
cometchat-native-customization | Text formatters, events, custom views |
cometchat-native-production | Server-side auth tokens + user management |
cometchat-native-troubleshooting | pod install fails, build errors, missing modules, privacy manifest rejection |