# Building a React Native Chat App with CometChat UI Kit

Learn how to build and launch a real-time React Native chat app quickly using CometChat’s UI Kits. This step-by-step tutorial covers integration, theme customization, and component tailoring to create a fully functional, personalized chat experience.

Haris Kumar • Jan 13, 2025

Prerequisites

Prerequisites Getting started Building the chat app interface Adding advanced functionalities on top of the existing features 1\. Setting up notifications  2\. Setting up advanced moderation 3\. Using Webhooks to extend your chat app's functionality Customizing Your Chat Interface Component customization  Testing Your Chat App Testing checklist Deployment Preparation Final Deployment Checklist

Building chat apps in React Native from scratch is an ambitious task that comes with a range of complex challenges, such as:

- Setting up and managing real-time infrastructure for message exchanges

- Implementing reliable message delivery and storage systems

- Building presence tracking and typing indicators

- Creating secure user authentication flows

- Ensuring scalability for multiple users and heavy usage

- Developing an intuitive and responsive chat interface

- Managing cross-platform compatibility between iOS and Android

CometChat's [React Native UI Kit](https://www.cometchat.com/react-native-chat-ui-kit) simplifies this process by offering pre-built, customizable chat components that eliminate the heavy lifting, while still allowing developers the flexibility to align the chat experience with their unique app requirements.

To better understand how CometChat can accelerate your development process, here's a quick comparison of building a chat app using the traditional DIY approach versus leveraging CometChat:

| Feature/Component | DIY Approach | With CometChat |
| --- | --- | --- |
| Setup Time | 3-4 weeks for basic features | 2-3 days for full implementation |
| Backend Infrastructure | Build and maintain servers, databases, and WebSocket connections. Handle platform-specific optimizations. | Fully managed by CometChat. Scalable infrastructure. |
| Real-time Communication | Implement WebSocket handling, connection management, and platform lifecycle events | Built into SDK with automatic handling. |
| User Management | Create user systems, presence tracking, and online status | Pre-built user management system with cross-platform presence handling |
| Message Delivery | Build queuing, retry logic, and delivery confirmation systems | Automatic with guaranteed delivery and offline support |
| Media Handling | Implement platform-specific upload, storage, and delivery systems | Built-in media handling with CDN support and native pickers |
| Security | Implement encryption, authentication, and access control | Enterprise-grade security included |
| UI Components | Build all chat interface components from scratch | Pre-built, customizable UI components |
| Scaling | Handle infrastructure scaling, load balancing, and performance optimization | Automatically scales with your usage |
| Notifications | Build a separate engine to manage notifications | Out-of-the-box setup for push, email, and SMS notifications via dashboard, plus user preference controls |
| Moderation | Build or integrate with third-party moderation tools | AI-powered filters, conditional rules engine, and a centralized dashboard for admin control |
| Analytics | Develop custom reports and metrics for user engagement and chat activity | Built-in analytics with ready-made reports to track engagement, usage and chat activity |

In this tutorial, we'll build a full-featured chat application that supports:

- One-on-one and group messaging

- Rich media sharing (images, files, links)

- Message threads and reactions

- User presence and typing indicators

- Push notifications

- Message moderation

- Custom UI theming

By the end of this tutorial, you'll have a production-ready chat application that can be easily customized to match your brand and requirements, all powered by CometChat's robust backend infrastructure.

## Prerequisites

Before starting, ensure you have:

- Node.js (version 14 or higher)

- React Native CLI (version 0.70.0 or higher)

- JDK 11

- npm or yarn package manager

- Android Studio (for Android development)

- Xcode 13+ (for iOS development)

- iOS 12.0 or later (for iOS development)

- CometChat account (free plan available)

## Getting started

### Step 1: Create a New React Native Project

Start by creating a fresh React Native project using the CLI. The command will set up a new React Native project with the latest stable version.

|     |     |
| --- | --- |
| `1` | ```<br>npx react-native init ChatingApp<br>``` |
| `2` | ```<br>``` |
| `3` | ```<br>cd ChatingApp<br>``` |

### Step 2. Install Required Dependencies

Install the following packages with their specific versions to ensure compatibility:

|     |     |
| --- | --- |
| `1` | ```<br># Install CometChat Core packages<br>``` |
| `2` | ```<br>npm i @cometchat/chat-uikit-react-native<br>``` |
| `3` | ```<br>npm i @cometchat/chat-sdk-react-native<br>``` |
| `4` | ```<br>``` |
| `5` | ```<br># Install required dependencies<br>``` |
| `6` | ```<br>npm i @react-native-async-storage/async-storage@1.17.10<br>``` |
| `7` | ```<br>npm i @react-native-clipboard/clipboard@1.13.2<br>``` |
| `8` | ```<br>npm i @react-native-community/datetimepicker@7.6.4<br>``` |
| `9` | ```<br>``` |
| `10` | ```<br># Install navigation dependencies<br>``` |
| `11` | ```<br>npm i @react-navigation/native@6.1.6<br>``` |
| `12` | ```<br>npm i @react-navigation/native-stack@6.9.12<br>``` |
| `13` | ```<br>npm i react-native-screens@3.20.0<br>``` |
| `14` | ```<br>``` |

For React Native 0.76 and above, also install:

|     |     |
| --- | --- |
| `1` | ```<br>npm i dayjs<br>``` |

iOS Setup

|     |     |
| --- | --- |
| `1` | ```<br>cd ios<br>``` |
| `2` | ```<br>``` |
| `3` | ```<br>pod install<br>``` |
| `4` | ```<br>``` |
| `5` | ```<br>cd ..<br>``` |

### Step 3: Configure Project Settings

For optimal compatibility, disable the new React Native architecture for both platforms:

For iOS, update your **_ios/Podfile:_**

|     |     |
| --- | --- |
| `1` | ```<br>ENV['RCT_NEW_ARCH_ENABLED']='0'<br>``` |

For Android, update **_android/gradle.properties:_**

|     |     |
| --- | --- |
| `1` | ```<br>newArchEnabled=false<br>``` |

### Step 4: Add Platform Permissions

Add the necessary permissions,

- For Android: Update AndroidManifest.xml with storage, vibration, and internet permissions

- For iOS: Update Info.plist with required permissions

#### Android Permissions

Add the following permissions to **_android/app/src/main/AndroidManifest.xml_** inside the **_<manifest>_** tag:

|     |     |
| --- | --- |
| `1` | ```<br><!-- Basic Permissions --><br>``` |
| `2` | ```<br><uses-permission android:name="android.permission.INTERNET" /><br>``` |
| `3` | ```<br><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><br>``` |
| `4` | ```<br><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><br>``` |
| `5` | ```<br><uses-permission android:name="android.permission.VIBRATE" /><br>``` |
| `6` | ```<br>``` |
| `7` | ```<br><!-- Additional Permissions (Required for Voice/Video Calls) --><br>``` |
| `8` | ```<br><uses-permission android:name="android.permission.CAMERA" /><br>``` |
| `9` | ```<br><uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /><br>``` |
| `10` | ```<br><uses-permission android:name="android.permission.RECORD_AUDIO" /><br>``` |
| `11` | ```<br><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><br>``` |

Note: For Android development, ensure **_ANDROID\_HOME_** environment variable is properly set or configure **_sdk.dir_** in **_local.properties_**

#### iOS Permissions

Add the following keys to **_ios/YourAppName/Info.plist_** inside the **_<dict>_** tag:

|     |     |
| --- | --- |
| `1` | ```<br><dict><br>``` |
| `2` | ```<br><!-- Basic Permissions --><br>``` |
| `3` | ```<br><key>NSPhotoLibraryUsageDescription</key><br>``` |
| `4` | ```<br><string>Allow access to photo library for sharing images and media in chat</string><br>``` |
| `5` | ```<br>``` |
| `6` | ```<br><!-- Additional Permissions (Required for Voice/Video Calls) --><br>``` |
| `7` | ```<br><key>NSCameraUsageDescription</key><br>``` |
| `8` | ```<br><string>Camera access is required for video calls</string><br>``` |
| `9` | ```<br><key>NSMicrophoneUsageDescription</key><br>``` |
| `10` | ```<br><string>Microphone access is required for voice calls</string><br>``` |
| `11` | ```<br>``` |
| `12` | ```<br><!-- Optional: If implementing push notifications --><br>``` |
| `13` | ```<br><key>NSAppTransportSecurity</key><br>``` |
| `14` | ```<br><dict><br>``` |
| `15` | ```<br>    <key>NSAllowsArbitraryLoads</key><br>``` |
| `16` | ```<br>    <true/><br>``` |
| `17` | ```<br></dict><br>``` |

Note: Customize the permission description strings to match your app's specific use cases and privacy policy.

### Step 5: Initialize CometChat

Create a configuration file to handle CometChat initialization. This setup includes:

- Permission handling for Android

- CometChat UI Kit initialization

- Error handling and initialization status

- Environment-specific configuration

Create a new file **_src/config/cometChat.js_**

|     |     |
| --- | --- |
| `1` | ```<br>import { CometChatUIKit, UIKitSettings } from '@cometchat/chat-uikit-react-native';<br>``` |
| `2` | ```<br>import { CometChat } from '@cometchat/chat-sdk-react-native';<br>``` |
| `3` | ```<br>import { Platform, PermissionsAndroid } from 'react-native';<br>``` |
| `4` | ```<br>``` |
| `5` | ```<br>// Replace these with your actual CometChat credentials<br>``` |
| `6` | ```<br>const APP_ID = 'YOUR_APP_ID_HERE';<br>``` |
| `7` | ```<br>const AUTH_KEY = 'YOUR_AUTH_KEY_HERE';<br>``` |
| `8` | ```<br>const REGION = 'YOUR_REGION_HERE';<br>``` |
| `9` | ```<br>``` |
| `10` | ```<br>// Validation helper<br>``` |
| `11` | ```<br>const validateConfig = () => {<br>``` |
| `12` | ```<br>  const missing = [];<br>``` |
| `13` | ```<br>  if (!APP_ID) missing.push('APP_ID');<br>``` |
| `14` | ```<br>  if (!AUTH_KEY) missing.push('AUTH_KEY');<br>``` |
| `15` | ```<br>  if (!REGION) missing.push('REGION');<br>``` |
| `16` | ```<br>  <br>``` |
| `17` | ```<br>  if (missing.length > 0) {<br>``` |
| `18` | ```<br>    throw new Error(`Missing required CometChat configuration: ${missing.join(', ')}`);<br>``` |
| `19` | ```<br>  }<br>``` |
| `20` | ```<br>};<br>``` |
| `21` | ```<br>``` |
| `22` | ```<br>const requestAndroidPermissions = async () => {<br>``` |
| `23` | ```<br>  if (Platform.OS === 'android') {<br>``` |
| `24` | ```<br>    try {<br>``` |
| `25` | ```<br>      const permissions = [<br>``` |\
| `26` | ```<br>        PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,<br>``` |\
| `27` | ```<br>        PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE,<br>``` |\
| `28` | ```<br>        PermissionsAndroid.PERMISSIONS.CAMERA,<br>``` |\
| `29` | ```<br>        PermissionsAndroid.PERMISSIONS.RECORD_AUDIO,<br>``` |\
| `30` | ```<br>      ];<br>``` |
| `31` | ```<br>``` |
| `32` | ```<br>      await PermissionsAndroid.requestMultiple(permissions);<br>``` |
| `33` | ```<br>    } catch (error) {<br>``` |
| `34` | ```<br>      console.error('Error requesting permissions:', error);<br>``` |
| `35` | ```<br>      throw error;<br>``` |
| `36` | ```<br>    }<br>``` |
| `37` | ```<br>  }<br>``` |
| `38` | ```<br>};<br>``` |
| `39` | ```<br>``` |
| `40` | ```<br>export const initializeCometChat = async () => {<br>``` |
| `41` | ```<br>  try {<br>``` |
| `42` | ```<br>    validateConfig();<br>``` |
| `43` | ```<br>    await requestAndroidPermissions();<br>``` |
| `44` | ```<br>``` |
| `45` | ```<br>    const uiKitSettings = new UIKitSettings({<br>``` |
| `46` | ```<br>      appID: APP_ID,<br>``` |
| `47` | ```<br>      authKey: AUTH_KEY,<br>``` |
| `48` | ```<br>      region: REGION,<br>``` |
| `49` | ```<br>      subscriptionType: CometChat.AppSettings.SUBSCRIPTION_TYPE_ALL_USERS,<br>``` |
| `50` | ```<br>    });<br>``` |
| `51` | ```<br>``` |
| `52` | ```<br>    await CometChatUIKit.init(uiKitSettings);<br>``` |
| `53` | ```<br>    console.log('CometChat initialized successfully');<br>``` |
| `54` | ```<br>    return true;<br>``` |
| `55` | ```<br>  } catch (error) {<br>``` |
| `56` | ```<br>    console.error('CometChat initialization failed:', error);<br>``` |
| `57` | ```<br>    throw error;<br>``` |
| `58` | ```<br>  }<br>``` |
| `59` | ```<br>};<br>``` |
| `60` | ```<br>``` |
| `61` | ```<br>export const loginWithUID = async (uid) => {<br>``` |
| `62` | ```<br>  if (!uid?.trim()) {<br>``` |
| `63` | ```<br>    throw new Error('Invalid user ID provided');<br>``` |
| `64` | ```<br>  }<br>``` |
| `65` | ```<br>``` |
| `66` | ```<br>  try {<br>``` |
| `67` | ```<br>    const user = await CometChatUIKit.login({ uid });<br>``` |
| `68` | ```<br>    if (!user) {<br>``` |
| `69` | ```<br>      throw new Error('Login failed - no user returned');<br>``` |
| `70` | ```<br>    }<br>``` |
| `71` | ```<br>    return user;<br>``` |
| `72` | ```<br>  } catch (error) {<br>``` |
| `73` | ```<br>    console.error('Login failed:', error);<br>``` |
| `74` | ```<br>    throw new Error(error.message || 'Authentication failed');<br>``` |
| `75` | ```<br>  }<br>``` |
| `76` | ```<br>};<br>``` |
| `77` | ```<br>``` |
| `78` | ```<br>export const logout = async () => {<br>``` |
| `79` | ```<br>  try {<br>``` |
| `80` | ```<br>    await CometChatUIKit.logout();<br>``` |
| `81` | ```<br>    return true;<br>``` |
| `82` | ```<br>  } catch (error) {<br>``` |
| `83` | ```<br>    console.error('Logout failed:', error);<br>``` |
| `84` | ```<br>    throw error;<br>``` |
| `85` | ```<br>  }<br>``` |
| `86` | ```<br>};<br>``` |

Important security note: The Auth Key should only be used during development. For production, use Auth Tokens for secure user authentication.

## Building the chat app interface

### 1\. User login and authentication flow

Before diving into building the chat interface, we'll create a login component that authenticates users with CometChat using their unique User ID. This authentication system is fundamental as it establishes a secure connection between users and CometChat servers, managing user sessions and ensuring proper access to conversations.

We'll create a login form that:

- Accepts a User ID input from the user

- Authenticates the User ID against CometChat's backend

- Handles loading states and error messages

- Establishes a user session upon successful login

#### Step 1: Create a new authentication context to manage login state

Create **_src/context/AuthContext.js:_**

|     |     |
| --- | --- |
| `1` | ```<br>import React, { createContext, useState, useContext, useEffect } from 'react';<br>``` |
| `2` | ```<br>import AsyncStorage from '@react-native-async-storage/async-storage';<br>``` |
| `3` | ```<br>import { loginWithUID, logout } from '../config/cometChat';<br>``` |
| `4` | ```<br>import NetInfo from '@react-native-community/netinfo';<br>``` |
| `5` | ```<br>``` |
| `6` | ```<br>const AuthContext = createContext(null);<br>``` |
| `7` | ```<br>const USER_STORAGE_KEY = '@user_data';<br>``` |
| `8` | ```<br>``` |
| `9` | ```<br>export const AuthProvider = ({ children }) => {<br>``` |
| `10` | ```<br>  const [user, setUser] = useState(null);<br>``` |
| `11` | ```<br>  const [loading, setLoading] = useState(true);<br>``` |
| `12` | ```<br>  const [isConnected, setIsConnected] = useState(true);<br>``` |
| `13` | ```<br>``` |
| `14` | ```<br>  useEffect(() => {<br>``` |
| `15` | ```<br>    const unsubscribe = NetInfo.addEventListener(state => {<br>``` |
| `16` | ```<br>      setIsConnected(state.isConnected);<br>``` |
| `17` | ```<br>    });<br>``` |
| `18` | ```<br>``` |
| `19` | ```<br>    checkAuthState();<br>``` |
| `20` | ```<br>    <br>``` |
| `21` | ```<br>    return () => {<br>``` |
| `22` | ```<br>      unsubscribe();<br>``` |
| `23` | ```<br>    };<br>``` |
| `24` | ```<br>  }, []);<br>``` |
| `25` | ```<br>``` |
| `26` | ```<br>  const handleLogin = async (uid) => {<br>``` |
| `27` | ```<br>    setLoading(true);<br>``` |
| `28` | ```<br>    try {<br>``` |
| `29` | ```<br>      const user = await loginWithUID(uid);<br>``` |
| `30` | ```<br>      await AsyncStorage.setItem(USER_STORAGE_KEY, JSON.stringify(user));<br>``` |
| `31` | ```<br>      setUser(user);<br>``` |
| `32` | ```<br>      return user;<br>``` |
| `33` | ```<br>    } catch (error) {<br>``` |
| `34` | ```<br>      throw error;<br>``` |
| `35` | ```<br>    } finally {<br>``` |
| `36` | ```<br>      setLoading(false);<br>``` |
| `37` | ```<br>    }<br>``` |
| `38` | ```<br>  };<br>``` |
| `39` | ```<br>``` |
| `40` | ```<br>  const handleLogout = async () => {<br>``` |
| `41` | ```<br>    setLoading(true);<br>``` |
| `42` | ```<br>    try {<br>``` |
| `43` | ```<br>      await logout();<br>``` |
| `44` | ```<br>      await AsyncStorage.removeItem(USER_STORAGE_KEY);<br>``` |
| `45` | ```<br>      setUser(null);<br>``` |
| `46` | ```<br>    } catch (error) {<br>``` |
| `47` | ```<br>      console.error('Logout error:', error);<br>``` |
| `48` | ```<br>      throw error;<br>``` |
| `49` | ```<br>    } finally {<br>``` |
| `50` | ```<br>      setLoading(false);<br>``` |
| `51` | ```<br>    }<br>``` |
| `52` | ```<br>  };<br>``` |
| `53` | ```<br>``` |
| `54` | ```<br>  const checkAuthState = async () => {<br>``` |
| `55` | ```<br>    try {<br>``` |
| `56` | ```<br>      const savedUser = await AsyncStorage.getItem(USER_STORAGE_KEY);<br>``` |
| `57` | ```<br>      if (savedUser) {<br>``` |
| `58` | ```<br>        const userData = JSON.parse(savedUser);<br>``` |
| `59` | ```<br>        // Re-authenticate with CometChat<br>``` |
| `60` | ```<br>        await loginWithUID(userData.uid);<br>``` |
| `61` | ```<br>        setUser(userData);<br>``` |
| `62` | ```<br>      }<br>``` |
| `63` | ```<br>    } catch (error) {<br>``` |
| `64` | ```<br>      console.error('Auth state check failed:', error);<br>``` |
| `65` | ```<br>      await AsyncStorage.removeItem(USER_STORAGE_KEY);<br>``` |
| `66` | ```<br>    } finally {<br>``` |
| `67` | ```<br>      setLoading(false);<br>``` |
| `68` | ```<br>    }<br>``` |
| `69` | ```<br>  };<br>``` |
| `70` | ```<br>``` |
| `71` | ```<br>  return (<br>``` |
| `72` | ```<br>    <AuthContext.Provider <br>``` |
| `73` | ```<br>      value={{ <br>``` |
| `74` | ```<br>        user, <br>``` |
| `75` | ```<br>        loading,<br>``` |
| `76` | ```<br>        isConnected, <br>``` |
| `77` | ```<br>        login: handleLogin, <br>``` |
| `78` | ```<br>        logout: handleLogout,<br>``` |
| `79` | ```<br>        checkAuthState <br>``` |
| `80` | ```<br>      }}<br>``` |
| `81` | ```<br>    ><br>``` |
| `82` | ```<br>      {children}<br>``` |
| `83` | ```<br>    </AuthContext.Provider><br>``` |
| `84` | ```<br>  );<br>``` |
| `85` | ```<br>};<br>``` |
| `86` | ```<br>``` |
| `87` | ```<br>export const useAuth = () => {<br>``` |
| `88` | ```<br>  const context = useContext(AuthContext);<br>``` |
| `89` | ```<br>  if (!context) {<br>``` |
| `90` | ```<br>    throw new Error('useAuth must be used within an AuthProvider');<br>``` |
| `91` | ```<br>  }<br>``` |
| `92` | ```<br>  return context;<br>``` |
| `93` | ```<br>};<br>``` |

### Step 2: Create Error Boundary Component

Create **_src/components/ErrorBoundary.js_**

|     |     |
| --- | --- |
| `1` | ```<br>import React from 'react';<br>``` |
| `2` | ```<br>import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';<br>``` |
| `3` | ```<br>``` |
| `4` | ```<br>class ErrorBoundary extends React.Component {<br>``` |
| `5` | ```<br>  state = { hasError: false, error: null };<br>``` |
| `6` | ```<br>``` |
| `7` | ```<br>  static getDerivedStateFromError(error) {<br>``` |
| `8` | ```<br>    return { hasError: true, error };<br>``` |
| `9` | ```<br>  }<br>``` |
| `10` | ```<br>``` |
| `11` | ```<br>  componentDidCatch(error, errorInfo) {<br>``` |
| `12` | ```<br>    console.error('Chat app error:', error, errorInfo);<br>``` |
| `13` | ```<br>  }<br>``` |
| `14` | ```<br>``` |
| `15` | ```<br>  render() {<br>``` |
| `16` | ```<br>    if (this.state.hasError) {<br>``` |
| `17` | ```<br>      return (<br>``` |
| `18` | ```<br>        <View style={styles.errorContainer}><br>``` |
| `19` | ```<br>          <Text style={styles.errorText}>Something went wrong</Text><br>``` |
| `20` | ```<br>          <TouchableOpacity <br>``` |
| `21` | ```<br>            onPress={() => this.setState({ hasError: false })}<br>``` |
| `22` | ```<br>            style={styles.retryButton}<br>``` |
| `23` | ```<br>          ><br>``` |
| `24` | ```<br>            <Text style={styles.retryText}>Try Again</Text><br>``` |
| `25` | ```<br>          </TouchableOpacity><br>``` |
| `26` | ```<br>        </View><br>``` |
| `27` | ```<br>      );<br>``` |
| `28` | ```<br>    }<br>``` |
| `29` | ```<br>``` |
| `30` | ```<br>    return this.props.children;<br>``` |
| `31` | ```<br>  }<br>``` |
| `32` | ```<br>}<br>``` |
| `33` | ```<br>``` |
| `34` | ```<br>const styles = StyleSheet.create({<br>``` |
| `35` | ```<br>  errorContainer: {<br>``` |
| `36` | ```<br>    flex: 1,<br>``` |
| `37` | ```<br>    justifyContent: 'center',<br>``` |
| `38` | ```<br>    alignItems: 'center',<br>``` |
| `39` | ```<br>    padding: 20,<br>``` |
| `40` | ```<br>  },<br>``` |
| `41` | ```<br>  errorText: {<br>``` |
| `42` | ```<br>    fontSize: 18,<br>``` |
| `43` | ```<br>    marginBottom: 20,<br>``` |
| `44` | ```<br>    textAlign: 'center',<br>``` |
| `45` | ```<br>  },<br>``` |
| `46` | ```<br>  retryButton: {<br>``` |
| `47` | ```<br>    backgroundColor: '#2196F3',<br>``` |
| `48` | ```<br>    padding: 12,<br>``` |
| `49` | ```<br>    borderRadius: 8,<br>``` |
| `50` | ```<br>  },<br>``` |
| `51` | ```<br>  retryText: {<br>``` |
| `52` | ```<br>    color: '#fff',<br>``` |
| `53` | ```<br>    fontSize: 16,<br>``` |
| `54` | ```<br>  },<br>``` |
| `55` | ```<br>});<br>``` |
| `56` | ```<br>``` |
| `57` | ```<br>export default ErrorBoundary;<br>``` |

### Step 3: Create a Login screen

Create **_src/screens/LoginScreen.js_**:

|     |     |
| --- | --- |
| `1` | ```<br>import React, { useState } from 'react';<br>``` |
| `2` | ```<br>import {<br>``` |
| `3` | ```<br>  View,<br>``` |
| `4` | ```<br>  TextInput,<br>``` |
| `5` | ```<br>  TouchableOpacity,<br>``` |
| `6` | ```<br>  Text,<br>``` |
| `7` | ```<br>  StyleSheet,<br>``` |
| `8` | ```<br>  ActivityIndicator,<br>``` |
| `9` | ```<br>  Alert,<br>``` |
| `10` | ```<br>  KeyboardAvoidingView,<br>``` |
| `11` | ```<br>  Platform,<br>``` |
| `12` | ```<br>} from 'react-native';<br>``` |
| `13` | ```<br>import { useAuth } from '../context/AuthContext';<br>``` |
| `14` | ```<br>``` |
| `15` | ```<br>const LoginScreen = () => {<br>``` |
| `16` | ```<br>  const [uid, setUid] = useState('');<br>``` |
| `17` | ```<br>  const { login, loading, isConnected } = useAuth();<br>``` |
| `18` | ```<br>``` |
| `19` | ```<br>  const handleLogin = async () => {<br>``` |
| `20` | ```<br>    if (!isConnected) {<br>``` |
| `21` | ```<br>      Alert.alert('Error', 'No internet connection');<br>``` |
| `22` | ```<br>      return;<br>``` |
| `23` | ```<br>    }<br>``` |
| `24` | ```<br>``` |
| `25` | ```<br>    if (!uid.trim()) {<br>``` |
| `26` | ```<br>      Alert.alert('Error', 'Please enter a User ID');<br>``` |
| `27` | ```<br>      return;<br>``` |
| `28` | ```<br>    }<br>``` |
| `29` | ```<br>``` |
| `30` | ```<br>    try {<br>``` |
| `31` | ```<br>      await login(uid.trim());<br>``` |
| `32` | ```<br>    } catch (error) {<br>``` |
| `33` | ```<br>      Alert.alert(<br>``` |
| `34` | ```<br>        'Login Failed',<br>``` |
| `35` | ```<br>        error.message || 'Unable to connect. Please check your User ID and try again.'<br>``` |
| `36` | ```<br>      );<br>``` |
| `37` | ```<br>    }<br>``` |
| `38` | ```<br>  };<br>``` |
| `39` | ```<br>``` |
| `40` | ```<br>  return (<br>``` |
| `41` | ```<br>    <KeyboardAvoidingView <br>``` |
| `42` | ```<br>      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}<br>``` |
| `43` | ```<br>      style={styles.container}<br>``` |
| `44` | ```<br>    ><br>``` |
| `45` | ```<br>      <View style={styles.card}><br>``` |
| `46` | ```<br>        <Text style={styles.title}>Welcome</Text><br>``` |
| `47` | ```<br>        <TextInput<br>``` |
| `48` | ```<br>          style={styles.input}<br>``` |
| `49` | ```<br>          placeholder="Enter User ID"<br>``` |
| `50` | ```<br>          value={uid}<br>``` |
| `51` | ```<br>          onChangeText={setUid}<br>``` |
| `52` | ```<br>          autoCapitalize="none"<br>``` |
| `53` | ```<br>          editable={!loading}<br>``` |
| `54` | ```<br>          autoCorrect={false}<br>``` |
| `55` | ```<br>        /><br>``` |
| `56` | ```<br>``` |
| `57` | ```<br>        <TouchableOpacity<br>``` |
| `58` | ```<br>          style={[styles.button, loading && styles.buttonDisabled]}<br>``` |
| `59` | ```<br>          onPress={handleLogin}<br>``` |
| `60` | ```<br>          disabled={loading}<br>``` |
| `61` | ```<br>        ><br>``` |
| `62` | ```<br>          {loading ? (<br>``` |
| `63` | ```<br>            <ActivityIndicator color="#FFFFFF" /><br>``` |
| `64` | ```<br>          ) : (<br>``` |
| `65` | ```<br>            <Text style={styles.buttonText}>Login</Text><br>``` |
| `66` | ```<br>          )}<br>``` |
| `67` | ```<br>        </TouchableOpacity><br>``` |
| `68` | ```<br>``` |
| `69` | ```<br>        {!isConnected && (<br>``` |
| `70` | ```<br>          <Text style={styles.errorText}><br>``` |
| `71` | ```<br>            No internet connection. Please check your network.<br>``` |
| `72` | ```<br>          </Text><br>``` |
| `73` | ```<br>        )}<br>``` |
| `74` | ```<br>``` |
| `75` | ```<br>        <Text style={styles.hint}><br>``` |
| `76` | ```<br>          Try: superhero1, superhero2, superhero3, superhero4, or superhero5<br>``` |
| `77` | ```<br>        </Text><br>``` |
| `78` | ```<br>      </View><br>``` |
| `79` | ```<br>    </KeyboardAvoidingView><br>``` |
| `80` | ```<br>  );<br>``` |
| `81` | ```<br>};<br>``` |
| `82` | ```<br>``` |
| `83` | ```<br>const styles = StyleSheet.create({<br>``` |
| `84` | ```<br>  // ... styles remain the same ...<br>``` |
| `85` | ```<br>});<br>``` |
| `86` | ```<br>``` |
| `87` | ```<br>export default LoginScreen;<br>``` |

### 2\. Building the chat interface

|     |     |
| --- | --- |
| `1` | ```<br>import React from 'react';<br>``` |
| `2` | ```<br>import { SafeAreaView, StyleSheet, View, Text } from 'react-native';<br>``` |
| `3` | ```<br>import { CometChatConversationsWithMessages } from '@cometchat/chat-uikit-react-native';<br>``` |
| `4` | ```<br>import { useAuth } from '../context/AuthContext';<br>``` |
| `5` | ```<br>``` |
| `6` | ```<br>const ChatScreen = ({ theme }) => {<br>``` |
| `7` | ```<br>  const { logout, isConnected } = useAuth();<br>``` |
| `8` | ```<br>``` |
| `9` | ```<br>  if (!isConnected) {<br>``` |
| `10` | ```<br>    return (<br>``` |
| `11` | ```<br>      <View style={styles.errorContainer}><br>``` |
| `12` | ```<br>        <Text style={styles.errorText}><br>``` |
| `13` | ```<br>          No internet connection. Please check your network.<br>``` |
| `14` | ```<br>        </Text><br>``` |
| `15` | ```<br>      </View><br>``` |
| `16` | ```<br>    );<br>``` |
| `17` | ```<br>  }<br>``` |
| `18` | ```<br>``` |
| `19` | ```<br>  return (<br>``` |
| `20` | ```<br>    <SafeAreaView style={styles.container}><br>``` |
| `21` | ```<br>      <CometChatConversationsWithMessages <br>``` |
| `22` | ```<br>        theme={theme}<br>``` |
| `23` | ```<br>        onSignOut={logout}<br>``` |
| `24` | ```<br>      /><br>``` |
| `25` | ```<br>    </SafeAreaView><br>``` |
| `26` | ```<br>  );<br>``` |
| `27` | ```<br>};<br>``` |
| `28` | ```<br>``` |
| `29` | ```<br>const styles = StyleSheet.create({<br>``` |
| `30` | ```<br>  container: {<br>``` |
| `31` | ```<br>    flex: 1,<br>``` |
| `32` | ```<br>    backgroundColor: '#fff',<br>``` |
| `33` | ```<br>  },<br>``` |
| `34` | ```<br>  errorContainer: {<br>``` |
| `35` | ```<br>    flex: 1,<br>``` |
| `36` | ```<br>    justifyContent: 'center',<br>``` |
| `37` | ```<br>    alignItems: 'center',<br>``` |
| `38` | ```<br>    padding: 20,<br>``` |
| `39` | ```<br>  },<br>``` |
| `40` | ```<br>  errorText: {<br>``` |
| `41` | ```<br>    textAlign: 'center',<br>``` |
| `42` | ```<br>    color: 'red',<br>``` |
| `43` | ```<br>  },<br>``` |
| `44` | ```<br>});<br>``` |
| `45` | ```<br>``` |
| `46` | ```<br>export default ChatScreen;<br>``` |

Update **_App.js:_**

|     |     |
| --- | --- |
| `1` | ```<br>import React, { useEffect, useState } from 'react';<br>``` |
| `2` | ```<br>import { SafeAreaView, View, ActivityIndicator, StyleSheet } from 'react-native';<br>``` |
| `3` | ```<br>import { NavigationContainer } from '@react-navigation/native';<br>``` |
| `4` | ```<br>import { createNativeStackNavigator } from '@react-navigation/native-stack';<br>``` |
| `5` | ```<br>import { initializeCometChat } from './src/config/cometChat';<br>``` |
| `6` | ```<br>import { AuthProvider, useAuth } from './src/context/AuthContext';<br>``` |
| `7` | ```<br>import LoginScreen from './src/screens/LoginScreen';<br>``` |
| `8` | ```<br>import ChatScreen from './src/screens/ChatScreen';<br>``` |
| `9` | ```<br>import ErrorBoundary from './src/components/ErrorBoundary';<br>``` |
| `10` | ```<br>import { initializeCustomTheme } from './src/config/theme';<br>``` |
| `11` | ```<br>``` |
| `12` | ```<br>const Stack = createNativeStackNavigator();<br>``` |
| `13` | ```<br>``` |
| `14` | ```<br>const LoadingScreen = () => (<br>``` |
| `15` | ```<br>  <View style={styles.loadingContainer}><br>``` |
| `16` | ```<br>    <ActivityIndicator size="large" color="#2196F3" /><br>``` |
| `17` | ```<br>  </View><br>``` |
| `18` | ```<br>);<br>``` |
| `19` | ```<br>``` |
| `20` | ```<br>const Navigation = () => {<br>``` |
| `21` | ```<br>  const { user, loading } = useAuth();<br>``` |
| `22` | ```<br>``` |
| `23` | ```<br>  if (loading) {<br>``` |
| `24` | ```<br>    return <LoadingScreen />;<br>``` |
| `25` | ```<br>  }<br>``` |
| `26` | ```<br>``` |
| `27` | ```<br>  return (<br>``` |
| `28` | ```<br>    <Stack.Navigator><br>``` |
| `29` | ```<br>      {!user ? (<br>``` |
| `30` | ```<br>        <Stack.Screen <br>``` |
| `31` | ```<br>          name="Login" <br>``` |
| `32` | ```<br>          component={LoginScreen} <br>``` |
| `33` | ```<br>          options={{ headerShown: false }}<br>``` |
| `34` | ```<br>        /><br>``` |
| `35` | ```<br>      ) : (<br>``` |
| `36` | ```<br>        <Stack.Screen<br>``` |
| `37` | ```<br>          name="Chat"<br>``` |
| `38` | ```<br>          component={ChatScreen}<br>``` |
| `39` | ```<br>          options={{ <br>``` |
| `40` | ```<br>            headerShown: false,<br>``` |
| `41` | ```<br>            // Prevent going back to login screen<br>``` |
| `42` | ```<br>            gestureEnabled: false,<br>``` |
| `43` | ```<br>          }}<br>``` |
| `44` | ```<br>        /><br>``` |
| `45` | ```<br>      )}<br>``` |
| `46` | ```<br>    </Stack.Navigator><br>``` |
| `47` | ```<br>  );<br>``` |
| `48` | ```<br>};<br>``` |
| `49` | ```<br>``` |
| `50` | ```<br>const App = () => {<br>``` |
| `51` | ```<br>  const [isInitialized, setIsInitialized] = useState(false);<br>``` |
| `52` | ```<br>  const [theme] = useState(() => initializeCustomTheme());<br>``` |
| `53` | ```<br>``` |
| `54` | ```<br>  useEffect(() => {<br>``` |
| `55` | ```<br>    const initialize = async () => {<br>``` |
| `56` | ```<br>      try {<br>``` |
| `57` | ```<br>        await initializeCometChat();<br>``` |
| `58` | ```<br>        setIsInitialized(true);<br>``` |
| `59` | ```<br>      } catch (error) {<br>``` |
| `60` | ```<br>        console.error('Failed to initialize CometChat:', error);<br>``` |
| `61` | ```<br>        // You might want to show an error message to the user here<br>``` |
| `62` | ```<br>      }<br>``` |
| `63` | ```<br>    };<br>``` |
| `64` | ```<br>``` |
| `65` | ```<br>    initialize();<br>``` |
| `66` | ```<br>  }, []);<br>``` |
| `67` | ```<br>``` |
| `68` | ```<br>  if (!isInitialized) {<br>``` |
| `69` | ```<br>    return <LoadingScreen />;<br>``` |
| `70` | ```<br>  }<br>``` |
| `71` | ```<br>``` |
| `72` | ```<br>  return (<br>``` |
| `73` | ```<br>    <ErrorBoundary><br>``` |
| `74` | ```<br>      <SafeAreaView style={styles.container}><br>``` |
| `75` | ```<br>        <AuthProvider><br>``` |
| `76` | ```<br>          <NavigationContainer><br>``` |
| `77` | ```<br>            <Navigation /><br>``` |
| `78` | ```<br>          </NavigationContainer><br>``` |
| `79` | ```<br>        </AuthProvider><br>``` |
| `80` | ```<br>      </SafeAreaView><br>``` |
| `81` | ```<br>    </ErrorBoundary><br>``` |
| `82` | ```<br>  );<br>``` |
| `83` | ```<br>};<br>``` |
| `84` | ```<br>``` |
| `85` | ```<br>const styles = StyleSheet.create({<br>``` |
| `86` | ```<br>  container: {<br>``` |
| `87` | ```<br>    flex: 1,<br>``` |
| `88` | ```<br>  },<br>``` |
| `89` | ```<br>  loadingContainer: {<br>``` |
| `90` | ```<br>    flex: 1,<br>``` |
| `91` | ```<br>    justifyContent: 'center',<br>``` |
| `92` | ```<br>    alignItems: 'center',<br>``` |
| `93` | ```<br>    backgroundColor: '#fff',<br>``` |
| `94` | ```<br>  },<br>``` |
| `95` | ```<br>});<br>``` |
| `96` | ```<br>``` |
| `97` | ```<br>export default App;<br>``` |

## Adding advanced functionalities on top of the existing features

## 1\. Setting up notifications

Now that your chat functionality is up and running, it's time to enable notifications so users never miss a message. CometChat’s Notifications module allows you to set up and manage notifications across multiple providers directly from the dashboard, ensuring centralized control and effortless configuration. You can easily integrate push notifications with APNs and FCM, email notifications with SendGrid, and SMS alerts with Twilio, or use webhooks to connect with any provider of your choice.

You can also enable notification preferences for your users, allowing them to customize when and how they receive notifications. Additionally, our customizable templates let you tailor notifications to your app’s tone and branding.

## 2\. Setting up advanced moderation

Next, we’ll explore CometChat’s powerful moderation capabilities, which you can enable using the CometChat SDK. These features are designed to help maintain a safe, respectful environment for users and allow you to moderate content in real time.

### Front-end moderation

- User reporting

Empower your users to report inappropriate behavior or content directly from the chat interface.

- User blocking

Allow users to block others, preventing unwanted interactions.

- Message flagging

Enables users to flag specific messages for review by administrators.

### Moderation queue:

Use our centralized moderation dashboard to give app admins an organized view of flagged messages, reported users, and blocked content. From this dashboard, admins can take real-time action on flagged content, issue user bans or warnings, and escalate issues for higher-level review, all in one place.

### Proactive content moderation:

Use our advanced AI filters to prevent harmful content from entering your chat. These filters automatically screen every message exchanged in your app for violations defined by you and take action based on the moderation rules you’ve set up with our rules engine.

CometChat’s rules engine lets you create custom rules to address common moderation scenarios. You can define which filters to use for identifying violations and specify how to handle them, such as blocking a user or removing them from a group. This helps you keep your app free from:

- Spam

Block unwanted or malicious content.

- Harassment

Automatically filter harmful messages or behavior.

- Image and Video Moderation

Analyze and filter media content to ensure it follows guidelines

## 3\. Using Webhooks to extend your chat app's functionality

Leverage webhooks to trigger actions based on key events like message exchanges, user interactions, group changes, and call activities. You can use them to automate tasks, integrate with external services, and optimize the chat experience in your app. You can configure these webhooks directly from the CometChat dashboard.

Here’s a list of available webhooks:

| Category | Webhook Event |
| --- | --- |
| Message | Message Sent, Edited, Deleted, Reacted, Mentioned, Delivered, Read |
| User | User Blocked, Unblocked, Connection Status Changed |
| Group | Group Created, Member Added, Member Left, Group Owner Changed, Member Kicked |
| Calling | Call Started, Call Ended, Call Initiated, Participant Joined, Participant Left, Call Recording Generated |

## Customizing Your Chat Interface

One of the key advantages of using CometChat's UI Kit is the ability to easily customize the appearance and behavior of your chat interface. In this section, we'll explore how to customize your chat app at both the theme level and component level.

### Theme Level Customization

CometChat's theme system allows you to make app-wide changes to colors, typography, and other visual elements. The theme consists of two main components:

#### 1\. Palette: Controls the app's color scheme

The palette defines colors for various elements, with built-in support for light and dark modes to ensure seamless theme switching. Key color categories include:

- **Primary Colors**: Represent your main brand identity.

- **Secondary Colors**: Complement and support primary colors.

- **Accent Colors**: Highlight important elements for emphasis.

- **Background Colors**: Define the appearance of the app's background.

- **Error Colors**: Indicate error states and messages.

Each color category can be configured with separate values for light and dark modes.

#### 2\. Typography: Defines text styles and appearances

Typography allows you to customize the text across your app for different use cases. The key text styles include:

- **Heading**: For main headings and prominent titles.

- **Name**: Styled for usernames and contact names.

- **Title (1 & 2**): Applied to section titles and headers.

- **Subtitle (1 & 2**): For supporting text under titles.

- **Text (1 & 2)**: General text styles for content and messages.

- **Caption (1 & 2)**: For smaller, supplementary text elements.

All these customizations can be applied through the theme configuration, ensuring consistent styling across your entire chat application.

### Setting Up Custom Theme

Create a new file **_src/config/theme.js_** to manage your theme customizations:

|     |     |
| --- | --- |
| `1` | ```<br>// src/config/theme.js<br>``` |
| `2` | ```<br>``` |
| `3` | ```<br>import { CometChatTheme } from '@cometchat/chat-uikit-react-native';<br>``` |
| `4` | ```<br>``` |
| `5` | ```<br>export const initializeCustomTheme = () => {<br>``` |
| `6` | ```<br>  // Create a new theme instance<br>``` |
| `7` | ```<br>  const customTheme = new CometChatTheme({});<br>``` |
| `8` | ```<br>``` |
| `9` | ```<br>  // Customize primary colors (e.g., main brand colors)<br>``` |
| `10` | ```<br>  customTheme.palette.setPrimary({<br>``` |
| `11` | ```<br>    light: "YOUR_PRIMARY_COLOR_LIGHT",  // e.g., "#2196F3"<br>``` |
| `12` | ```<br>    dark: "YOUR_PRIMARY_COLOR_DARK"     // e.g., "#1976D2"<br>``` |
| `13` | ```<br>  });<br>``` |
| `14` | ```<br>``` |
| `15` | ```<br>  // Customize secondary colors (e.g., accent or complementary colors)<br>``` |
| `16` | ```<br>  customTheme.palette.setSecondary({<br>``` |
| `17` | ```<br>    light: "YOUR_SECONDARY_COLOR_LIGHT", // e.g., "#FF4081"<br>``` |
| `18` | ```<br>    dark: "YOUR_SECONDARY_COLOR_DARK"    // e.g., "#F50057"<br>``` |
| `19` | ```<br>  });<br>``` |
| `20` | ```<br>``` |
| `21` | ```<br>  // Customize accent colors (used for highlights and emphasis)<br>``` |
| `22` | ```<br>  customTheme.palette.setAccent({<br>``` |
| `23` | ```<br>    light: "YOUR_ACCENT_COLOR_LIGHT",    // e.g., "#00BCD4"<br>``` |
| `24` | ```<br>    dark: "YOUR_ACCENT_COLOR_DARK"       // e.g., "#0097A7"<br>``` |
| `25` | ```<br>  });<br>``` |
| `26` | ```<br>``` |
| `27` | ```<br>  // Optional: Customize background colors<br>``` |
| `28` | ```<br>  customTheme.palette.setBackground({<br>``` |
| `29` | ```<br>    light: "YOUR_BACKGROUND_COLOR_LIGHT", // e.g., "#FFFFFF"<br>``` |
| `30` | ```<br>    dark: "YOUR_BACKGROUND_COLOR_DARK"    // e.g., "#121212"<br>``` |
| `31` | ```<br>  });<br>``` |
| `32` | ```<br>``` |
| `33` | ```<br>  // Optional: Customize typography<br>``` |
| `34` | ```<br>  customTheme.typography.setName({ <br>``` |
| `35` | ```<br>    fontSize: "YOUR_FONT_SIZE",           // e.g., 16<br>``` |
| `36` | ```<br>    fontWeight: "YOUR_FONT_WEIGHT"        // e.g., "600"<br>``` |
| `37` | ```<br>  });<br>``` |
| `38` | ```<br>``` |
| `39` | ```<br>  // Optional: Customize other text styles<br>``` |
| `40` | ```<br>  customTheme.typography.setText1({ <br>``` |
| `41` | ```<br>    fontSize: "YOUR_TEXT_SIZE",           // e.g., 14<br>``` |
| `42` | ```<br>    fontWeight: "YOUR_TEXT_WEIGHT"        // e.g., "400"<br>``` |
| `43` | ```<br>  });<br>``` |
| `44` | ```<br>``` |
| `45` | ```<br>  return customTheme;<br>``` |
| `46` | ```<br>};<br>``` |

### Applying the custom theme

Update your **_App.js_** to initialize and apply the custom theme:

|     |     |
| --- | --- |
| `1` | ```<br>// App.js<br>``` |
| `2` | ```<br>``` |
| `3` | ```<br>import React, { useEffect, useState } from 'react';<br>``` |
| `4` | ```<br>import { SafeAreaView, View, ActivityIndicator } from 'react-native';<br>``` |
| `5` | ```<br>import { initializeCometChat } from './src/config/cometChat';<br>``` |
| `6` | ```<br>import { initializeCustomTheme } from './src/config/theme';<br>``` |
| `7` | ```<br>import { AuthProvider, useAuth } from './src/context/AuthContext';<br>``` |
| `8` | ```<br>import LoginScreen from './src/screens/LoginScreen';<br>``` |
| `9` | ```<br>import ChatScreen from './src/screens/ChatScreen';<br>``` |
| `10` | ```<br>``` |
| `11` | ```<br>const LoadingScreen = () => (<br>``` |
| `12` | ```<br>  <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}><br>``` |
| `13` | ```<br>    <ActivityIndicator size="large" /><br>``` |
| `14` | ```<br>  </View><br>``` |
| `15` | ```<br>);<br>``` |
| `16` | ```<br>``` |
| `17` | ```<br>const ChatApp = () => {<br>``` |
| `18` | ```<br>  const { user, loading, checkAuthState } = useAuth();<br>``` |
| `19` | ```<br>  const [theme] = useState(() => initializeCustomTheme());<br>``` |
| `20` | ```<br>``` |
| `21` | ```<br>  useEffect(() => {<br>``` |
| `22` | ```<br>    checkAuthState();<br>``` |
| `23` | ```<br>  }, []);<br>``` |
| `24` | ```<br>``` |
| `25` | ```<br>  if (loading) {<br>``` |
| `26` | ```<br>    return <LoadingScreen />;<br>``` |
| `27` | ```<br>  }<br>``` |
| `28` | ```<br>``` |
| `29` | ```<br>  if (!user) {<br>``` |
| `30` | ```<br>    return <LoginScreen />;<br>``` |
| `31` | ```<br>  }<br>``` |
| `32` | ```<br>``` |
| `33` | ```<br>  return <ChatScreen theme={theme} />;<br>``` |
| `34` | ```<br>};<br>``` |
| `35` | ```<br>``` |
| `36` | ```<br>const App = () => {<br>``` |
| `37` | ```<br>  const [isInitialized, setIsInitialized] = useState(false);<br>``` |
| `38` | ```<br>``` |
| `39` | ```<br>  useEffect(() => {<br>``` |
| `40` | ```<br>    const initialize = async () => {<br>``` |
| `41` | ```<br>      const success = await initializeCometChat();<br>``` |
| `42` | ```<br>      setIsInitialized(success);<br>``` |
| `43` | ```<br>    };<br>``` |
| `44` | ```<br>``` |
| `45` | ```<br>    initialize();<br>``` |
| `46` | ```<br>  }, []);<br>``` |
| `47` | ```<br>``` |
| `48` | ```<br>  if (!isInitialized) {<br>``` |
| `49` | ```<br>    return <LoadingScreen />;<br>``` |
| `50` | ```<br>  }<br>``` |
| `51` | ```<br>``` |
| `52` | ```<br>  return (<br>``` |
| `53` | ```<br>    <AuthProvider><br>``` |
| `54` | ```<br>      <ChatApp /><br>``` |
| `55` | ```<br>    </AuthProvider><br>``` |
| `56` | ```<br>  );<br>``` |
| `57` | ```<br>};<br>``` |
| `58` | ```<br>``` |
| `59` | ```<br>export default App;<br>``` |

## Component customization

### Conversations component customisation

The Conversations component displays all conversations related to the currently logged-in user. To ensure the component aligns with your app's design and branding, you can easily customize its appearance by adjusting the properties exposed by the **ConversationsStyle** class. Below, you'll find a table outlining all the properties you can modify to tailor the component to your specific needs.

| Category | Customizable Elements | Description |
| --- | --- | --- |
| Layout & Appearance | List Item View | Height, width, background colors, hover states |
| Layout & Appearance | Avatar Display | Shape, size, border radius, default icons |
| Layout & Appearance | Status Indicators | Size, color, position, visibility |
| Content Display | Conversation Title | Font style, size, color, formatting |
| Content Display | Last Message Preview | Length, style, condensed view options |
| Content Display | Timestamps | Format, position, localization |
| Interactive Elements | Search Bar | Visibility, placeholder text, icon customization |
| Interactive Elements | Selection Mode | Single/multi select, selection indicators |
| Interactive Elements | Empty States | Custom messages, actions, layouts |

For more advanced customization, you can define custom views within the Conversations component. This allows you to fully control and tailor each element of the interface, such as layouts, views, and UI elements, ensuring that the component perfectly matches your app's overall aesthetics and functionality. .

#### Messages component customization

The **Messages** component is a central feature of our chat interface, responsible for rendering the area where messages are exchanged between users and groups. It plays a key role in managing the real-time flow of messages within the app. Below, you'll find a table outlining all the properties you can modify to tailor the messages component to your specific needs

| Category | Customizable Elements | Description |
| --- | --- | --- |
| Message Bubbles | Layout | Size, shape, padding, margins |
| Message Bubbles | Styling | Colors, borders, shadows |
| Message Bubbles | Alignment | Left/right positioning, spacing |
| Message Types | Text Messages | Font styles, sizes, colors |
| Message Types | Media Messages | Preview size, player controls |
| Message Types | Link Previews | Size, layout, information display |
| Interactive Features | Reactions | Position, size, layout |
| Interactive Features | Thread Replies | Thread view, indicators |
| Interactive Features | Message Actions | Custom actions, menus |
| Metadata Display | Timestamps | Format, position |
| Metadata Display | Read Receipts | Style, position, indicators |
| Metadata Display | Typing Indicators | Animation, position |

## Testing Your Chat App

Before you deploy your project to production, it's important to properly structure, test, and verify that everything works as expected. In this section, we'll walk you through how to run the project locally, test key features, and ensure that everything is functioning properly before moving to deployment.

Your final project structure should look like this

## Testing checklist

### 1\. Configuration Check

Verify CometChat credentials in **_cometChat.js_**

Ensure all required permissions are in place for both platforms

### 2\. Functional Testing

Test the following flows to ensure your chat app is working correctly:

#### Authentication Flow

- Try logging in with valid test user credentials

- Verify error handling with invalid credentials

- Check if login session persists after app restart

#### Chat Functionality

Login with two different test users on different devices

- Start a conversation between these users

- Verify real-time message delivery

- Test media sharing capabilities

- Check user online/offline status updates

#### UI/Theme Verification

- Verify your custom theme is applied correctly

- Test on both iOS and Android devices

- Check all interactive elements (buttons, inputs)

## Deployment Preparation

### Android Release

#### Step 1: Generate Release Keystore

|     |     |
| --- | --- |
| `1` | ```<br>keytool -genkey -v -keystore android/app/release.keystore \<br>``` |
| `2` | ```<br>  -alias your-key-alias \<br>``` |
| `3` | ```<br>  -keyalg RSA \<br>``` |
| `4` | ```<br>  -keysize 2048 \<br>``` |
| `5` | ```<br>  -validity 10000<br>``` |

#### Step 2: Configure Gradle Properties

Create or update **_android/gradle.properties_**

|     |     |
| --- | --- |
| `1` | ```<br>MYAPP_RELEASE_STORE_FILE=release.keystore<br>``` |
| `2` | ```<br>MYAPP_RELEASE_KEY_ALIAS=your-key-alias<br>``` |
| `3` | ```<br>MYAPP_RELEASE_STORE_PASSWORD=*****<br>``` |
| `4` | ```<br>MYAPP_RELEASE_KEY_PASSWORD=*****<br>``` |

#### Step 3: Update build.gradle

Edit **_android/app/build.gradle_**:

|     |     |
| --- | --- |
| `1` | ```<br>android {<br>``` |
| `2` | ```<br>    ...<br>``` |
| `3` | ```<br>    signingConfigs {<br>``` |
| `4` | ```<br>        release {<br>``` |
| `5` | ```<br>            storeFile file(MYAPP_RELEASE_STORE_FILE)<br>``` |
| `6` | ```<br>            storePassword MYAPP_RELEASE_STORE_PASSWORD<br>``` |
| `7` | ```<br>            keyAlias MYAPP_RELEASE_KEY_ALIAS<br>``` |
| `8` | ```<br>            keyPassword MYAPP_RELEASE_KEY_PASSWORD<br>``` |
| `9` | ```<br>        }<br>``` |
| `10` | ```<br>    }<br>``` |
| `11` | ```<br>    buildTypes {<br>``` |
| `12` | ```<br>        release {<br>``` |
| `13` | ```<br>            ...<br>``` |
| `14` | ```<br>            signingConfig signingConfigs.release<br>``` |
| `15` | ```<br>            shrinkResources true<br>``` |
| `16` | ```<br>            minifyEnabled true<br>``` |
| `17` | ```<br>            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'<br>``` |
| `18` | ```<br>        }<br>``` |
| `19` | ```<br>    }<br>``` |
| `20` | ```<br>}<br>``` |

#### Step 4: Create Final Release Build

|     |     |
| --- | --- |
| `1` | ```<br>cd android<br>``` |
| `2` | ```<br>./gradlew bundleRelease  # For Play Store<br>``` |
| `3` | ```<br>./gradlew assembleRelease  # For APK<br>``` |

The release APK will be in **_android/app/build/outputs/apk/release/_**

### iOS Release

#### Xcode Configuration

- Open Xcode

- Select your project

- Set Build Configuration to Release

- Update version and build numbers

- Configure signing certificates

#### Archive and Upload

- Select Generic iOS Device as the build target

- Choose Product > Archive

- In Archives window, click "Distribute App"

- Follow App Store distribution workflow

## Final Deployment Checklist

- Verify production CometChat credentials

- Remove any test API keys

- Configure proper error reporting

Haris Kumar

Lead Content Strategist , CometChat

Haris brings nearly half a decade of expertise in B2B SaaS content marketing, where he excels at developing strategic content that drives engagement and supports business growth. His deep understanding of the SaaS landscape allows him to craft compelling narratives that resonate with target audiences. Outside of his professional pursuits, Haris enjoys reading, trying out new dishes and watching new movies!
