{"id":"63882dbb-6307-4fb3-b9e9-8f8a731460c9","shortId":"bEJX5x","kind":"skill","title":"cometchat-native-expo-patterns","tagline":"Integration patterns for Expo managed workflow — app.json config, permissions, gesture handler setup, env vars, Expo Router file-based routing subsection.","description":"## Purpose\n\nTeaches Claude how to integrate CometChat into an Expo managed workflow project. Covers:\n\n- Installing the full peer-dependency set (not just the UI Kit)\n- Configuring `app.json` permissions for iOS + Android\n- Wiring the provider chain in `App.tsx` with all four wrappers\n- Optional calling SDK setup\n- Env vars via `expo-constants` or `.env`\n- **Expo Router subsection** (file-based routing)\n- Prebuild + run cadence\n\n**Read `cometchat-native-core` first** (init/login/wrapper chain + anti-patterns), then `cometchat-native-components` (prop reference), then `cometchat-native-placement` (where chat goes).\n\nGround truth: `docs/ui-kit/react-native/expo-integration.mdx`, `expo-conversation.mdx`, `expo-one-to-one-chat.mdx`, `expo-tab-based-chat.mdx`, and `examples/SampleAppExpo/`.\n\n---\n\n## Use this skill when\n\n- Project has `expo` in `package.json` dependencies\n- `app.json` / `app.config.js` exists at the root\n- `package.json` `main` field references `expo` (e.g. `\"main\": \"index.js\"` with an Expo-style entry)\n- The user says \"Expo\", \"Expo Router\", \"managed workflow\", or \"EAS\"\n\n**Do NOT use this skill when:**\n- The project has an `ios/` + `android/` folder at the root (that's bare RN → use `cometchat-native-bare-patterns`)\n- The user says \"bare React Native\", \"React Native CLI\", or \"ejected\"\n\n---\n\n## Hard prerequisite — Expo Go is NOT supported\n\nThe CometChat UI Kit depends on native modules that can't be shimmed. This means:\n\n- **Expo Go won't load your app** — you'll see \"Main module field cannot be resolved\" or similar\n- You must build a **development client** (`eas build --profile development` or `expo run:ios` / `expo run:android`)\n- Or install in a plain Expo simulator via prebuild\n\nThe first build can take 5-15 minutes. Subsequent runs are fast via the dev client.\n\nBefore integrating, confirm the user has either:\n- `eas-cli` installed and an EAS account, OR\n- Xcode + Android Studio for local prebuilds\n\nIf neither, stop and ask them to set one up. Don't waste their time installing packages that won't run.\n\n---\n\n## Step 1 — Install dependencies\n\nThe UI Kit has a long peer-dep tail. Install them all in one shot so Expo's resolver doesn't miss a native module during prebuild:\n\n```bash\n# Core SDK + UI Kit\nnpm install @cometchat/chat-sdk-react-native\nnpm install @cometchat/chat-uikit-react-native\n\n# Required peer deps (all natively-linked)\nnpx expo install \\\n  @react-native-async-storage/async-storage \\\n  @react-native-clipboard/clipboard \\\n  @react-native-community/datetimepicker \\\n  react-native-gesture-handler \\\n  react-native-localize \\\n  react-native-safe-area-context \\\n  react-native-svg \\\n  react-native-video\n\n# dayjs + punycode — no native code but required by the kit\nnpm install dayjs punycode\n```\n\n**Why `npx expo install` for the natively-linked deps?** `expo install` picks versions compatible with the project's Expo SDK. Using `npm install` directly can land incompatible versions that break prebuild.\n\n### Optional — calling SDK\n\nIf the user's flow includes voice / video calls (the `cometchat-native-features` skill's § Calls gates this):\n\n```bash\nnpm install @cometchat/calls-sdk-react-native\nnpx expo install \\\n  @react-native-community/netinfo \\\n  react-native-background-timer \\\n  react-native-callstats \\\n  react-native-webrtc\n```\n\nSkip these until the user actually wants calls. Adding WebRTC to an Expo project bloats the prebuild and requires extra permissions — don't speculatively enable it.\n\n---\n\n## Step 2 — Configure app.json\n\nAdd iOS + Android permissions so the kit's attachments, camera, mic, and media features work. Merge into existing `expo.ios.infoPlist` / `expo.android.permissions` — do not replace anything the user already has.\n\n```json\n{\n  \"expo\": {\n    \"ios\": {\n      \"infoPlist\": {\n        \"NSCameraUsageDescription\": \"Allow camera access to send photos and make video calls\",\n        \"NSMicrophoneUsageDescription\": \"Allow microphone access to send voice messages and make calls\",\n        \"NSPhotoLibraryUsageDescription\": \"Allow photo library access to send photos\",\n        \"NSPhotoLibraryAddUsageDescription\": \"Allow saving photos from chat to your library\"\n      }\n    },\n    \"android\": {\n      \"permissions\": [\n        \"android.permission.INTERNET\",\n        \"android.permission.ACCESS_NETWORK_STATE\",\n        \"android.permission.CAMERA\",\n        \"android.permission.RECORD_AUDIO\",\n        \"android.permission.MODIFY_AUDIO_SETTINGS\",\n        \"android.permission.READ_EXTERNAL_STORAGE\",\n        \"android.permission.WRITE_EXTERNAL_STORAGE\",\n        \"android.permission.VIBRATE\",\n        \"android.permission.READ_MEDIA_IMAGES\",\n        \"android.permission.READ_MEDIA_VIDEO\",\n        \"android.permission.READ_MEDIA_AUDIO\"\n      ]\n    }\n  }\n}\n```\n\n**Permission-string best practice**: the iOS `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.\n\n### If the project uses `app.config.js` or `app.config.ts`\n\nMerge the same fields into the exported config. Don't switch the project from JS to JSON unless the user asks — respect their setup.\n\n---\n\n## Step 3 — Wire the provider chain in App.tsx\n\nExpo projects use the same four-wrapper chain as bare RN (see `cometchat-native-core` § 3). The difference is the entry file — Expo uses `App.tsx` (or `index.ts` + `registerRootComponent`) rather than bare's `index.js` + `AppRegistry`.\n\n```tsx\n// App.tsx\nimport \"react-native-gesture-handler\";   // MUST be the first import\nimport React from \"react\";\nimport { GestureHandlerRootView } from \"react-native-gesture-handler\";\nimport { SafeAreaProvider } from \"react-native-safe-area-context\";\nimport { CometChatThemeProvider } from \"@cometchat/chat-uikit-react-native\";\nimport { CometChatProvider } from \"./src/providers/CometChatProvider\";\nimport { AppNavigator } from \"./src/navigation/AppNavigator\";\nimport Constants from \"expo-constants\";\n\nconst extra = Constants.expoConfig?.extra ?? {};\n\nexport default function App() {\n  return (\n    <GestureHandlerRootView style={{ flex: 1 }}>\n      <SafeAreaProvider>\n        <CometChatThemeProvider>\n          <CometChatProvider\n            appId={extra.COMETCHAT_APP_ID}\n            region={extra.COMETCHAT_REGION}\n            authKey={extra.COMETCHAT_AUTH_KEY}\n            uid=\"cometchat-uid-1\"   // dev mode only\n          >\n            <AppNavigator />\n          </CometChatProvider>\n        </CometChatThemeProvider>\n      </SafeAreaProvider>\n    </GestureHandlerRootView>\n  );\n}\n```\n\nThe `CometChatProvider` itself is defined per `cometchat-native-core` § 6 — reuse that implementation; don't invent another one.\n\n### `import \"react-native-gesture-handler\"` must be first\n\nEven before React. Expo's entry-file hot reload otherwise loses the gesture handler patch and the composer / bottom-sheet gestures silently disable.\n\n```ts\n// At the top of App.tsx:\nimport \"react-native-gesture-handler\";\n// THEN everything else\nimport React from \"react\";\n```\n\n---\n\n## Step 4 — Env vars\n\nTwo options; pick one based on what the project already uses.\n\n### Option A — `app.json extra` + `expo-constants` (simple, recommended)\n\n```json\n{\n  \"expo\": {\n    \"extra\": {\n      \"COMETCHAT_APP_ID\": \"YOUR_APP_ID\",\n      \"COMETCHAT_REGION\": \"us\",\n      \"COMETCHAT_AUTH_KEY\": \"YOUR_AUTH_KEY\"\n    }\n  }\n}\n```\n\nRead via `expo-constants`:\n\n```tsx\nimport Constants from \"expo-constants\";\nconst { COMETCHAT_APP_ID, COMETCHAT_REGION, COMETCHAT_AUTH_KEY } = Constants.expoConfig?.extra ?? {};\n```\n\n**Warning**: these values end up in the client bundle. Never put `REST_API_KEY` or any server-side secret in `expo.extra` — use a backend (see `cometchat-native-production`).\n\n**⚠️ Dev-client manifest caching trap (validated 2026-05-14).** `Constants.expoConfig?.extra` reads from the manifest the **dev client baked in at `expo prebuild` time** — NOT from the live `app.json`. Editing `app.json → expo.extra` and reloading the app does NOT pick up new values; the dev client keeps serving the prebuild-time snapshot. Two workarounds:\n\n- **For dev iteration on credentials**: hardcode in `src/config/*.ts` (Metro hot-bundles source changes immediately):\n  ```ts\n  // src/config/cometchat.ts\n  export const COMETCHAT_APP_ID = \"...\";\n  export const COMETCHAT_REGION = \"us\";\n  export const COMETCHAT_AUTH_KEY = \"...\";\n  ```\n- **For one-shot setup or production**: after every `app.json → extra` change, run\n  ```bash\n  npx expo prebuild --clean && npx expo run:android\n  ```\n  to regenerate the manifest snapshot.\n\nSilent failure mode: app reads stale App ID / Auth Key / Region → login fails with cryptic errors (`User not found`, region mismatch). Hit on the rn-existing cohort 2026-05-14; spent ~30 min before realizing the manifest cache was serving an old App ID.\n\n### Option B — `.env` + `expo-dotenv` / SDK-native `.env` support\n\nExpo SDK 50+ supports `.env` out of the box via `EXPO_PUBLIC_*` prefix:\n\n```\n# .env\nEXPO_PUBLIC_COMETCHAT_APP_ID=your_app_id\nEXPO_PUBLIC_COMETCHAT_REGION=us\nEXPO_PUBLIC_COMETCHAT_AUTH_KEY=your_auth_key\n```\n\nRead directly via `process.env.EXPO_PUBLIC_COMETCHAT_APP_ID` anywhere in your app. Any variable WITHOUT the `EXPO_PUBLIC_` prefix is ONLY available in `app.config.js` / server scripts, not bundled — useful for REST API keys in backend-only code.\n\n### Which to choose\n\n- If the project already has `.env` — **Option B**.\n- If the project hasn't set up env vars at all — **Option A** (one file, no prefix rules).\n- Never mix both for the same variable — pick one place.\n\n---\n\n## Step 5 — Expo Router subsection\n\nExpo Router is a file-based alternative to `@react-navigation/*`. If the project has `app/` instead of (or alongside) `src/screens/`, they're using Expo Router.\n\nDetect Expo Router by checking `package.json` for `expo-router` in dependencies, and `app.json` for the `\"expo-router\"` plugin.\n\n### 5a — Router entry (`app/_layout.tsx`)\n\nIn Expo Router, the `app/_layout.tsx` file is the root layout — wrap the provider chain here instead of in `App.tsx`.\n\n```tsx\n// app/_layout.tsx\nimport \"react-native-gesture-handler\";\nimport { GestureHandlerRootView } from \"react-native-gesture-handler\";\nimport { SafeAreaProvider } from \"react-native-safe-area-context\";\nimport { CometChatThemeProvider } from \"@cometchat/chat-uikit-react-native\";\nimport { CometChatProvider } from \"../src/providers/CometChatProvider\";\nimport { Slot } from \"expo-router\";\nimport Constants from \"expo-constants\";\n\nconst extra = Constants.expoConfig?.extra ?? {};\n\nexport default function RootLayout() {\n  return (\n    <GestureHandlerRootView style={{ flex: 1 }}>\n      <SafeAreaProvider>\n        <CometChatThemeProvider>\n          <CometChatProvider\n            appId={extra.COMETCHAT_APP_ID}\n            region={extra.COMETCHAT_REGION}\n            authKey={extra.COMETCHAT_AUTH_KEY}\n            uid=\"cometchat-uid-1\"\n          >\n            <Slot />   {/* Expo Router renders child routes here */}\n          </CometChatProvider>\n        </CometChatThemeProvider>\n      </SafeAreaProvider>\n    </GestureHandlerRootView>\n  );\n}\n```\n\n### 5b — Conversations + message route\n\n```\napp/\n  _layout.tsx          ← provider chain (above)\n  index.tsx            ← home (could be the conversations list)\n  messages/\n    [uid].tsx          ← dynamic route, one chat per uid\n```\n\n```tsx\n// app/index.tsx\nimport { CometChatConversations, CometChatUiKitConstants } from \"@cometchat/chat-uikit-react-native\";\nimport { router } from \"expo-router\";\n\nexport default function Home() {\n  return (\n    <CometChatConversations\n      onItemPress={(conversation) => {\n        const entity = conversation.getConversationWith();\n        const type = conversation.getConversationType();\n        if (type === CometChatUiKitConstants.ConversationTypeConstants.user) {\n          router.push(`/messages/${(entity as any).getUid()}`);\n        } else {\n          router.push(`/messages/group-${(entity as any).getGuid()}`);\n        }\n      }}\n    />\n  );\n}\n```\n\n```tsx\n// app/messages/[uid].tsx\nimport { useLocalSearchParams, router } from \"expo-router\";\nimport { useEffect, useState } from \"react\";\nimport { View } from \"react-native\";\nimport { CometChat } from \"@cometchat/chat-sdk-react-native\";\nimport {\n  CometChatMessageHeader,\n  CometChatMessageList,\n  CometChatMessageComposer,\n} from \"@cometchat/chat-uikit-react-native\";\n\nexport default function ChatScreen() {\n  const { uid } = useLocalSearchParams<{ uid: string }>();\n  const [user, setUser] = useState<CometChat.User | null>(null);\n\n  useEffect(() => {\n    if (!uid) return;\n    // Simple UID routing; group routing encodes differently in the index example above\n    CometChat.getUser(uid).then(setUser).catch(() => setUser(null));\n  }, [uid]);\n\n  if (!user) return null;\n\n  return (\n    <View style={{ flex: 1 }}>\n      <CometChatMessageHeader user={user} onBack={() => router.back()} showBackButton />\n      <CometChatMessageList user={user} hideReplyInThreadOption />\n      <CometChatMessageComposer user={user} />\n    </View>\n  );\n}\n```\n\n### 5c — Tabs in Expo Router (if the project uses them)\n\n```\napp/\n  _layout.tsx\n  (tabs)/\n    _layout.tsx        ← Tabs layout\n    chats.tsx\n    users.tsx\n    groups.tsx\n    calls.tsx\n  messages/\n    [uid].tsx\n```\n\n```tsx\n// app/(tabs)/_layout.tsx\nimport { Tabs } from \"expo-router\";\n\nexport default function TabsLayout() {\n  return (\n    <Tabs screenOptions={{ headerShown: false }}>\n      <Tabs.Screen name=\"chats\" options={{ title: \"Chats\" }} />\n      <Tabs.Screen name=\"users\" options={{ title: \"Users\" }} />\n      <Tabs.Screen name=\"groups\" options={{ title: \"Groups\" }} />\n      <Tabs.Screen name=\"calls\" options={{ title: \"Calls\" }} />\n    </Tabs>\n  );\n}\n```\n\nEach tab file renders a single list component (`CometChatConversations`, `CometChatUsers`, etc. — see `cometchat-native-placement` § 2 Bottom tab for the component choices) and pushes to `/messages/[uid]` on press.\n\n### Expo Router gotchas\n\n- **`unstable_settings`** in `_layout.tsx` can break deep linking if set incorrectly. Leave it alone unless you know you need it.\n- **Navigation between stack + tabs**: `router.push(\"/messages/abc\")` works from a tab. `router.back()` returns to the tab. No extra configuration needed.\n- **Search params**: use `useLocalSearchParams()` (not `useSearchParams()` — that's web-only).\n\n---\n\n## Step 6 — Prebuild + run\n\nBefore the first run, Expo needs to generate native projects:\n\n```bash\nnpx expo prebuild\n```\n\nThen run on the platform:\n\n```bash\n# iOS (requires Xcode)\nnpx expo run:ios\n\n# Android (requires Android Studio)\nnpx expo run:android\n\n# Or EAS for cloud builds\neas build --profile development --platform ios\n```\n\nSubsequent runs use `npx expo start` with the dev client — no rebuild needed unless native deps change.\n\n**When to rebuild vs. reload:**\n- Changed JS / JSX / TSX → no rebuild, just `r` to reload or save in Fast Refresh\n- Added / removed a native dependency → `npx expo prebuild --clean && npx expo run:ios`\n- Changed `app.json` permissions or plugins → `npx expo prebuild --clean`\n\n---\n\n## Step 7 — Verify integration\n\n```bash\nnpx tsc --noEmit    # TypeScript check\n```\n\nThen in the running app:\n\n1. Open the chat screen you wired\n2. Check that the keyboard opens when you tap the composer (gesture handler working)\n3. Tap the \"+\" attachment button — the action sheet should slide up (bottom sheet working)\n4. Send a message — it should appear immediately\n\nIf any of these don't work, see `cometchat-native-troubleshooting`.\n\n---\n\n## Hard rules\n\n1. **No Expo Go.** The user's project must use development builds. Detect early and tell the user if they're on Expo Go.\n2. **`import \"react-native-gesture-handler\"` is the first line of the entry file** (`App.tsx` or `app/_layout.tsx` for Expo Router). Not second. Not after any React import.\n3. **Install all peer deps via `npx expo install`**, not `npm install`, for native modules. Expo's resolver picks SDK-compatible versions.\n4. **Never commit `REST_API_KEY`** (or any server-side secret) to `app.json extra` — it ends up in the client bundle. Use a server endpoint (see `cometchat-native-production`).\n5. **Merge permissions into `app.json`, don't replace.** The user may already have permissions for other libraries; wipe them out and their other features break.\n6. **`npx expo prebuild --clean` after changing `app.json` permissions or adding native deps.** Without it, iOS + Android see the old config.\n7. **Every `<CometChatMessageList>` must include `hideReplyInThreadOption`** unless you're also wiring a full thread panel (see `cometchat-native-placement` § Hard rule 5).\n8. **The four-wrapper chain goes at the app root**, not per-screen (see `cometchat-native-core` § 3). For Expo Router, that's `app/_layout.tsx`. For plain Expo, that's `App.tsx`.\n\n---\n\n## Common questions\n\n**Q: Can I use `npx create-expo-app --template tabs`?**\nYes — the tabs template already has Expo Router set up. Just add the provider chain in `app/_layout.tsx` per § 5a and replace a tab's content with a CometChat component.\n\n**Q: Can I use SDK ≤49?**\nExpo SDK 49 may work but the CometChat peer deps target 50+ conventions. If the user is stuck on an older SDK, ask them to upgrade — or fall back to bare RN via `npx expo prebuild` + `cometchat-native-bare-patterns`.\n\n**Q: I'm seeing \"Main module field cannot be resolved\" when opening Expo Go.**\nThat's the \"Expo Go doesn't support native modules\" error. Build a dev client: `eas build --profile development` or `npx expo run:ios`.\n\n**Q: My app crashes on first push-notification receive.**\nPush notifications need additional setup (APNs + FCM + maybe `expo-notifications`). Out of scope for this skill — see `cometchat-native-troubleshooting` § Push notifications.\n\n---\n\n## Skill routing reference\n\n| Skill | When to route |\n|---|---|\n| `cometchat-native-core` | Init / login / wrapper chain / anti-patterns |\n| `cometchat-native-components` | Component prop reference |\n| `cometchat-native-placement` | Where chat goes (stack / tabs / modal / bottom sheet / embedded) |\n| `cometchat-native-expo-patterns` | This skill — Expo managed workflow specifics |\n| `cometchat-native-bare-patterns` | Bare RN (pod install, native modules, privacy manifest) |\n| `cometchat-native-features` | Calls, extensions, AI |\n| `cometchat-native-theming` | Theme customization |\n| `cometchat-native-customization` | Text formatters, events, custom views |\n| `cometchat-native-production` | Server-side auth tokens + user management |\n| `cometchat-native-troubleshooting` | Prebuild failures, Expo Go errors, keyboard issues, blank chat |","tags":["cometchat","native","expo","patterns","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs"],"capabilities":["skill","source-cometchat","skill-cometchat-native-expo-patterns","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-native-expo-patterns","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (18,619 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:54.425Z","embedding":null,"createdAt":"2026-05-07T13:05:14.627Z","updatedAt":"2026-05-18T19:04:54.425Z","lastSeenAt":"2026-05-18T19:04:54.425Z","tsv":"'-05':1026,1162 '-14':1027,1163 '-15':274 '/_layout.tsx':1645 '/async-storage':385 '/clipboard':390 '/datetimepicker':395 '/messages':1513,1711 '/messages/abc':1743 '/messages/group-':1520 '/netinfo':498 '/src/navigation/appnavigator':811 '/src/providers/cometchatprovider':807,1408 '1':328,830,847,1433,1450,1605,1892,1949 '2':539,1701,1899,1973 '2026':1025,1161 '3':723,747,1913,2001,2143 '30':1165 '4':924,1927,2024 '49':2203,2206 '5':273,1302,2055,2122 '50':1191,2215 '5a':1353,2187 '5b':1457 '5c':1619 '6':861,1769,2080 '7':1878,2101 '8':2123 'accept':690 'access':577,588,600,673 'account':298 'action':1919 'actual':517 'ad':520,1855,2090 'add':542,2180 'addit':2296 'ai':2385 'allow':575,586,597,605 'alon':1731 'alongsid':1326 'alreadi':568,936,1268,2066,2173 'also':2109 'altern':1313 'android':58,176,258,301,544,613,1127,1799,1801,1806,2096 'android.permission.access':616 'android.permission.camera':619 'android.permission.internet':615 'android.permission.modify':622 'android.permission.read':625,632,635,638 'android.permission.record':620 'android.permission.vibrate':631 'android.permission.write':628 'anoth':868 'anti':100,2333 'anti-pattern':99,2332 'anyth':565 'anywher':1232 'api':1000,1255,2028 'apn':2298 'app':230,825,834,951,954,979,1054,1094,1136,1139,1176,1206,1209,1230,1235,1322,1437,1461,1629,1643,1891,2132,2166,2285 'app.config.js':136,695,1247 'app.config.ts':697 'app.json':12,54,135,541,940,1047,1049,1115,1346,1869,2037,2059,2087 'app.tsx':64,729,756,767,909,1375,1988,2155 'app/_layout.tsx':1356,1361,1377,1990,2149,2185 'app/index.tsx':1483 'app/messages':1526 'appear':1933 'appid':832,1435 'appnavig':809 'appregistri':765 'area':409,798,1399 'ask':310,657,718,2226 'async':383 'attach':550,1916 'audio':621,623,640 'auth':841,960,963,984,1104,1141,1219,1222,1444,2408 'authkey':839,1442 'avail':1245 'b':1179,1272 'back':2232 'backend':1012,1259 'backend-on':1258 'background':502 'bake':1037 'bare':183,189,194,740,762,2234,2243,2369,2371 'base':24,86,931,1312 'bash':359,487,1119,1782,1791,1881 'best':644 'blank':2423 'bloat':526 'bottom':899,1702,1924,2352 'bottom-sheet':898 'box':1197 'break':463,1723,2079 'build':244,249,270,1811,1813,1960,2270,2275 'bundl':996,1085,1251,2045 'button':1917 'cach':1022,1171 'cadenc':90 'call':70,466,476,484,519,584,595,676,1681,1684,2383 'calls.tsx':1638 'callstat':507 'camera':551,576,672 'cannot':237,2252 'catch':1593 'chain':62,98,727,738,1370,1464,2128,2183,2331 'chang':1087,1117,1834,1840,1868,2086 'chat':115,609,1479,1663,1666,1895,2347,2424 'chats.tsx':1635 'chatscreen':1560 'check':1337,1886,1900 'child':1454 'choic':1707 'choos':1264 'claud':29 'clean':1123,1863,1876,2084 'cli':199,293 'client':247,283,995,1020,1036,1063,1827,2044,2273 'clipboard':389 'cloud':1810 'code':423,1261 'cohort':1160 'cometchat':2,33,93,104,111,187,210,479,744,845,858,950,956,959,978,981,983,1015,1093,1098,1103,1205,1213,1218,1229,1448,1548,1698,1944,2052,2117,2140,2196,2211,2241,2312,2325,2336,2343,2356,2367,2380,2387,2393,2402,2413 'cometchat-native-bare-pattern':186,2240,2366 'cometchat-native-compon':103,2335 'cometchat-native-cor':92,743,857,2139,2324 'cometchat-native-custom':2392 'cometchat-native-expo-pattern':1,2355 'cometchat-native-featur':478,2379 'cometchat-native-plac':110,1697,2116,2342 'cometchat-native-product':1014,2051,2401 'cometchat-native-them':2386 'cometchat-native-troubleshoot':1943,2311,2412 'cometchat-uid':844,1447 'cometchat.getuser':1589 'cometchat.user':1570 'cometchat/calls-sdk-react-native':490 'cometchat/chat-sdk-react-native':366,1550 'cometchat/chat-uikit-react-native':369,803,1404,1488,1556 'cometchatconvers':1485,1500,1693 'cometchatmessagecompos':1554,1616 'cometchatmessagehead':1552,1606 'cometchatmessagelist':1553,1612 'cometchatprovid':805,831,852,1406,1434 'cometchatthemeprovid':801,1402 'cometchatuikitconst':1486 'cometchatuikitconstants.conversationtypeconstants.user':1511 'cometchatus':1694 'commit':2026 'common':2156 'communiti':394,497 'compat':447,2022 'compon':106,1692,1706,2197,2338,2339 'compos':897,1909 'config':13,705,2100 'configur':53,540,1755 'confirm':286 'const':818,977,1092,1097,1102,1421,1503,1506,1561,1566 'constant':78,813,817,944,969,972,976,1416,1420 'constants.expoconfig':820,986,1028,1423 'content':2193 'context':410,799,1400 'convent':2216 'convers':1458,1471,1502 'conversation.getconversationtype':1508 'conversation.getconversationwith':1505 'copi':668 'core':95,360,746,860,2142,2327 'could':1468 'cover':40 'crash':2286 'creat':2164 'create-expo-app':2163 'credenti':1077 'cryptic':1147 'custom':2391,2395,2399 'dayj':419,431 'deep':1724 'default':823,1426,1496,1558,1653 'defin':855 'dep':339,372,442,1833,2005,2092,2213 'depend':46,134,213,330,1344,1859 'detect':1333,1961 'dev':282,848,1019,1035,1062,1074,1826,2272 'dev-client':1018 'develop':246,251,670,1815,1959,2277 'differ':749,1583 'direct':457,1225 'disabl':903 'docs/ui-kit/react-native/expo-integration.mdx':119 'doesn':351,2264 'dotenv':1183 'dynam':1476 'e.g':146 'ea':164,248,292,297,1808,1812,2274 'earli':1962 'eas-c':291 'edit':1048 'either':290 'eject':201 'els':918,1518 'embed':2354 'enabl':536 'encod':1582 'end':991,2040 'endpoint':2049 'entiti':1504,1514,1521 'entri':154,752,885,1355,1986 'entry-fil':884 'env':18,73,80,925,1180,1187,1193,1202,1270,1280 'error':1148,2269,2420 'etc':1695 'even':879 'event':2398 'everi':1114,2102 'everyth':917 'exampl':1587 'examples/sampleappexpo':124 'exist':137,559,1159 'expo':4,9,20,36,77,81,131,145,152,158,159,204,224,253,256,264,348,378,435,443,452,492,524,571,730,754,816,882,943,948,968,975,1040,1121,1125,1182,1189,1199,1203,1211,1216,1240,1303,1306,1331,1334,1341,1350,1358,1413,1419,1451,1493,1534,1622,1650,1715,1776,1784,1796,1804,1822,1861,1865,1874,1951,1971,1992,2008,2016,2082,2145,2152,2165,2175,2204,2238,2257,2262,2280,2302,2358,2362,2418 'expo-const':76,815,942,967,974,1418 'expo-conversation.mdx':120 'expo-dotenv':1181 'expo-notif':2301 'expo-one-to-one-chat.mdx':121 'expo-rout':1340,1349,1412,1492,1533,1649 'expo-styl':151 'expo-tab-based-chat.mdx':122 'expo.android.permissions':561 'expo.extra':1009,1050 'expo.ios.infoplist':560 'export':704,822,1091,1096,1101,1425,1495,1557,1652 'extens':2384 'extern':626,629 'extra':531,819,821,941,949,987,1029,1116,1422,1424,1754,2038 'extra.cometchat':833,837,840,1436,1440,1443 'face':667 'fail':1145 'failur':1134,2417 'fall':2231 'fals':1660 'fast':279,1853 'fcm':2299 'featur':481,555,2078,2382 'field':143,236,701,2251 'file':23,85,753,886,1287,1311,1362,1687,1987 'file-bas':22,84,1310 'fine':678 'first':96,269,777,878,1774,1982,2288 'flex':829,1432,1604 'flow':472 'folder':177 'formatt':2397 'found':1151 'four':67,736,2126 'four-wrapp':735,2125 'full':43,2112 'function':824,1427,1497,1559,1654 'gate':485 'generat':1779 'gestur':15,399,772,789,874,892,901,914,1382,1390,1910,1978 'gesturehandlerrootview':784,827,1385,1430 'getguid':1524 'getuid':1517 'go':205,225,1952,1972,2258,2263,2419 'goe':116,2129,2348 'gotcha':1717 'ground':117 'group':1580,1675,1678 'groups.tsx':1637 'handler':16,400,773,790,875,893,915,1383,1391,1911,1979 'hard':202,1947,2120 'hardcod':1078 'hasn':1276 'headershown':1659 'hidereplyinthreadopt':1615,2105 'hit':1154 'home':1467,1498 'hot':887,1084 'hot-bundl':1083 'id':835,952,955,980,1095,1140,1177,1207,1210,1231,1438 'imag':634 'immedi':1088,1934 'implement':864 'import':768,778,779,783,791,800,804,808,812,870,910,919,971,1378,1384,1392,1401,1405,1409,1415,1484,1489,1529,1536,1541,1547,1551,1646,1974,2000 'includ':473,2104 'incompat':460 'incorrect':1728 'index':1586 'index.js':148,764 'index.ts':758 'index.tsx':1466 'infoplist':573 'init':2328 'init/login/wrapper':97 'instal':41,260,294,321,329,341,365,368,379,430,436,444,456,489,493,2002,2009,2012,2374 'instead':1323,1372 'integr':6,32,285,1880 'invent':867 'io':57,175,255,543,572,647,656,1792,1798,1817,1867,2095,2282 'isn':682 'issu':2422 'iter':1075 'js':712,1841 'json':570,714,947 'jsx':1842 'keep':1064 'key':842,961,964,985,1001,1105,1142,1220,1223,1256,1445,2029 'keyboard':1903,2421 'kit':52,212,333,363,428,548 'know':1734 'land':459 'layout':1366,1634 'layout.tsx':1462,1630,1632,1721 'leav':1729 'librari':599,612,2071 'line':1983 'link':376,441,1725 'list':1472,1691 'live':1046 'll':232 'load':228 'local':304,404 'login':1144,2329 'long':336 'lose':890 'm':2247 'main':142,147,234,2249 'make':582,594 'manag':10,37,161,2363,2411 'manifest':1021,1033,1131,1170,2378 'may':2065,2207 'mayb':2300 'mean':223 'media':554,633,636,639,680 'merg':557,698,2056 'messag':592,1459,1473,1639,1930 'metro':1082 'mic':552 'microphon':587 'min':1166 'minut':275 'mismatch':1153 'miss':353 'mix':1292 'modal':2351 'mode':849,1135 'modul':216,235,356,2015,2250,2268,2376 'must':243,774,876,1957,2103 'name':1662,1668,1674,1680 'nativ':3,94,105,112,188,196,198,215,355,375,382,388,393,398,403,407,413,417,422,440,480,496,501,506,510,745,771,788,796,859,873,913,1016,1186,1381,1389,1397,1546,1699,1780,1832,1858,1945,1977,2014,2053,2091,2118,2141,2242,2267,2313,2326,2337,2344,2357,2368,2375,2381,2388,2394,2403,2414 'natively-link':374,439 'navig':1317,1738 'need':1736,1756,1777,1830,2295 'neither':307 'network':617 'never':997,1291,2025 'new':1059 'noemit':1884 'note':671 'notif':2291,2294,2303,2316 'npm':364,367,429,455,488,2011 'npx':377,434,491,1120,1124,1783,1795,1803,1821,1860,1864,1873,1882,2007,2081,2162,2237,2279 'nscamerausagedescript':574 'nsmicrophoneusagedescript':585 'nsphotolibraryaddusagedescript':604 'nsphotolibraryusagedescript':596 'null':1571,1572,1595,1600 'old':1175,2099 'older':2224 'onback':1609 'one':314,345,869,930,1108,1286,1299,1478 'one-shot':1107 'onitempress':1501 'open':1893,1904,2256 'option':69,465,928,938,1178,1271,1284,1664,1670,1676,1682 'otherwis':889 'packag':322 'package.json':133,141,1338 'panel':2114 'param':1758 'patch':894 'pattern':5,7,101,190,2244,2334,2359,2370 'peer':45,338,371,2004,2212 'peer-dep':337 'peer-depend':44 'per':856,1480,2136,2186 'per-screen':2135 'permiss':14,55,532,545,614,642,661,1870,2057,2068,2088 'permission-str':641 'photo':580,598,603,607 'pick':445,929,1057,1298,2019 'place':1300 'placement':113,1700,2119,2345 'plain':263,2151 'platform':1790,1816 'plugin':1352,1872 'pod':2373 'practic':645 'prebuild':88,267,305,358,464,528,1041,1068,1122,1770,1785,1862,1875,2083,2239,2416 'prebuild-tim':1067 'prefix':1201,1242,1289 'prerequisit':203 'press':1714 'privaci':2377 'process.env.expo':1227 'product':1017,1112,2054,2404 'profil':250,1814,2276 'project':39,129,172,450,525,693,710,731,935,1267,1275,1320,1626,1781,1956 'prompt':654 'prop':107,2340 'provid':61,726,1369,1463,2182 'public':1200,1204,1212,1217,1228,1241 'punycod':420,432 'purpos':27 'push':1709,2290,2293,2315 'push-notif':2289 'put':998 'q':2158,2198,2245,2283 'question':2157 'r':1847 'rather':760 're':1329,1969,2108 'react':195,197,381,387,392,397,402,406,412,416,495,500,505,509,770,780,782,787,795,872,881,912,920,922,1316,1380,1388,1396,1540,1545,1976,1999 'react-nat':1544 'react-native-async-storag':380 'react-native-background-tim':499 'react-native-callstat':504 'react-native-clipboard':386 'react-native-commun':391,494 'react-native-gesture-handl':396,769,786,871,911,1379,1387,1975 'react-native-loc':401 'react-native-safe-area-context':405,794,1395 'react-native-svg':411 'react-native-video':415 'react-native-webrtc':508 'react-navig':1315 'read':91,965,1030,1137,1224 'real':685 'realiz':1168 'reason':686 'rebuild':1829,1837,1845 'receiv':2292 'recommend':946 'refer':108,144,2319,2341 'refresh':1854 'regener':1129 'region':836,838,957,982,1099,1143,1152,1214,1439,1441 'registerrootcompon':759 'reload':888,1052,1839,1849 'remov':1856 'render':1453,1688 'replac':564,2062,2189 'requir':370,425,530,1793,1800 'resolv':239,350,2018,2254 'respect':719 'rest':999,1254,2027 'return':826,1429,1499,1576,1599,1601,1656,1749 'reus':862 'rn':184,741,1158,2235,2372 'rn-exist':1157 'root':140,180,1365,2133 'rootlayout':1428 'rout':25,87,1455,1460,1477,1579,1581,2318,2323 'router':21,82,160,1304,1307,1332,1335,1342,1351,1354,1359,1414,1452,1490,1494,1531,1535,1623,1651,1716,1993,2146,2176 'router.back':1610,1748 'router.push':1512,1519,1742 'rule':1290,1948,2121 'run':89,254,257,277,326,1118,1126,1771,1775,1787,1797,1805,1819,1866,1890,2281 'safe':408,797,1398 'safeareaprovid':792,1393 'save':606,1851 'say':157,193 'scope':2306 'screen':1896,2137 'screenopt':1658 'script':1249 'sdk':71,361,453,467,1185,1190,2021,2202,2205,2225 'sdk-compat':2020 'sdk-nativ':1184 'search':1757 'second':1995 'secret':1007,2035 'see':233,742,1013,1696,1942,2050,2097,2115,2138,2248,2310 'send':579,590,602,1928 'serv':1065,1173 'server':1005,1248,2033,2048,2406 'server-sid':1004,2032,2405 'set':47,313,624,1278,1719,1727,2177 'setup':17,72,721,1110,2297 'setus':1568,1592,1594 'sheet':900,1920,1925,2353 'shim':221 'shot':346,1109 'show':650 'showbackbutton':1611 'side':1006,2034,2407 'silent':902,1133 'similar':241 'simpl':945,1577 'simul':265 'singl':1690 'skill':127,169,482,2309,2317,2320,2361 'skill-cometchat-native-expo-patterns' 'skip':512 'slide':1922 'slot':1410 'snapshot':1070,1132 'sourc':1086 'source-cometchat' 'specif':2365 'specul':535 'spent':1164 'src/config':1080 'src/config/cometchat.ts':1090 'src/screens':1327 'stack':1740,2349 'stale':1138 'start':1823 'state':618 'step':327,538,722,923,1301,1768,1877 'stop':308 'storag':384,627,630 'string':643,649,1565 'stuck':2221 'studio':302,1802 'style':153,828,1431,1603 'subsect':26,83,1305 'subsequ':276,1818 'support':208,1188,1192,2266 'svg':414 'switch':708 'system':653 'tab':1620,1631,1633,1644,1647,1657,1686,1703,1741,1747,1752,2168,2171,2191,2350 'tabs.screen':1661,1667,1673,1679 'tabslayout':1655 'tail':340 'take':272 'tap':1907,1914 'target':2214 'teach':28 'tell':1964 'templat':2167,2172 'text':2396 'theme':2389,2390 'thread':2113 'time':320,1042,1069 'timer':503 'titl':1665,1671,1677,1683 'token':2409 'top':907 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'trap':1023 'troubleshoot':1946,2314,2415 'truth':118 'ts':904,1081,1089 'tsc':1883 'tsx':766,970,1376,1475,1482,1525,1528,1641,1642,1843 'two':927,1071 'type':1507,1510 'typescript':1885 'ui':51,211,332,362 'uid':843,846,1446,1449,1474,1481,1527,1562,1564,1575,1578,1590,1596,1640,1712 'unless':715,1732,1831,2106 'unstabl':1718 'upgrad':2229 'upload':681 'us':958,1100,1215 'usag':648 'use':125,167,185,454,694,732,755,937,1010,1252,1330,1627,1759,1820,1958,2046,2161,2201 'useeffect':1537,1573 'uselocalsearchparam':1530,1563,1760 'user':156,192,288,470,516,567,659,666,688,717,1149,1567,1598,1607,1608,1613,1614,1617,1618,1669,1672,1954,1966,2064,2219,2410 'user-fac':665 'users.tsx':1636 'usesearchparam':1762 'usest':1538,1569 'valid':1024 'valu':990,1060 'var':19,74,926,1281 'variabl':1237,1297 'verifi':1879 'version':446,461,2023 'via':75,266,280,966,1198,1226,2006,2236 'video':418,475,583,637,675 'view':1542,1602,2400 'voic':474,591 'vs':1838 'want':518 'warn':988 'wast':318 'web':1766 'web-on':1765 'webrtc':511,521 'wipe':2072 'wire':59,724,1898,2110 'without':1238,2093 'won':226,324 'work':556,1744,1912,1926,1941,2208 'workaround':1072 'workflow':11,38,162,2364 'would':689 'wrap':1367 'wrapper':68,737,2127,2330 'write':662 'xcode':300,1794 'yes':2169","prices":[{"id":"48e8b66e-092b-4a89-8da5-62cc0ab1bba4","listingId":"63882dbb-6307-4fb3-b9e9-8f8a731460c9","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T13:05:14.627Z"}],"sources":[{"listingId":"63882dbb-6307-4fb3-b9e9-8f8a731460c9","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-native-expo-patterns","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-native-expo-patterns","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:14.627Z","lastSeenAt":"2026-05-18T19:04:54.425Z"}],"details":{"listingId":"63882dbb-6307-4fb3-b9e9-8f8a731460c9","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-native-expo-patterns","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"23d0dd8bf6b7806610391815446be055f33bec29","skill_md_path":"skills/cometchat-native-expo-patterns/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-native-expo-patterns"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-native-expo-patterns","license":"MIT","description":"Integration patterns for Expo managed workflow — app.json config, permissions, gesture handler setup, env vars, Expo Router file-based routing subsection.","compatibility":"Node.js >=18; Expo SDK >=50; @cometchat/chat-uikit-react-native ^5"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-native-expo-patterns"},"updatedAt":"2026-05-18T19:04:54.425Z"}}