This tutorial teaches you how to integrate Sendbird UIKit for React Native in your mobile application.
This is what your app will look like when you've finished this tutorial:
Before you start, you need the following:
Next, create a user in your Sendbird application:
Let's start by creating a new React Native project. You can use either Expo or React Native CLI to do so.
npx create-expo-app@latest ChatApp --template blank-typescript
npx @react-native-community/cli@latest init ChatApp
Install the required packages using your preferred package manager.
npx expo install @sendbird/uikit-react-native \
@sendbird/chat \
date-fns \
react-native-safe-area-context \
@react-native-community/netinfo \
react-native-mmkv
yarn add @sendbird/uikit-react-native \
@sendbird/chat \
date-fns \
react-native-safe-area-context \
@react-native-community/netinfo \
react-native-mmkv
Choose the appropriate installation based on your environment:
npx expo install expo-image-picker \
expo-document-picker \
expo-media-library \
expo-file-system \
expo-clipboard \
expo-notifications \
expo-av \
expo-video-thumbnails \
expo-image-manipulator
npx expo prebuild
yarn add react-native-video \
react-native-permissions \
react-native-file-access \
react-native-image-picker \
react-native-document-picker \
react-native-create-thumbnail \
react-native-audio-recorder-player \
@react-native-clipboard/clipboard \
@react-native-camera-roll/camera-roll \
@react-native-firebase/app \
@react-native-firebase/messaging \
@bam.tech/react-native-image-resizer
npx pod-install
Implement the platform service interfaces that we provide to handle native features like file upload, notifications, and media playback.
import {
createExpoClipboardService,
createExpoFileService,
createExpoMediaService,
createExpoNotificationService,
createExpoPlayerService,
createExpoRecorderService,
SendbirdUIKitContainerProps
} from "@sendbird/uikit-react-native";
import * as ExpoClipboard from 'expo-clipboard';
import * as ExpoDocumentPicker from 'expo-document-picker';
import * as ExpoFS from 'expo-file-system';
import * as ExpoImagePicker from 'expo-image-picker';
import * as ExpoMediaLibrary from 'expo-media-library';
import * as ExpoNotifications from 'expo-notifications';
import * as ExpoAV from 'expo-av';
import * as ExpoVideoThumbnail from 'expo-video-thumbnails';
import * as ExpoImageManipulator from 'expo-image-manipulator';
const platformServices: SendbirdUIKitContainerProps['platformServices'] = {
clipboard: createExpoClipboardService(ExpoClipboard),
notification: createExpoNotificationService(ExpoNotifications),
file: createExpoFileService({
fsModule: ExpoFS,
imagePickerModule: ExpoImagePicker,
mediaLibraryModule: ExpoMediaLibrary,
documentPickerModule: ExpoDocumentPicker,
}),
media: createExpoMediaService({
avModule: ExpoAV,
thumbnailModule: ExpoVideoThumbnail,
imageManipulator: ExpoImageManipulator,
fsModule: ExpoFS,
}),
player: createExpoPlayerService({
avModule: ExpoAV,
}),
recorder: createExpoRecorderService({
avModule: ExpoAV,
}),
};
import {
createNativeClipboardService,
createNativeFileService,
createNativeMediaService,
createNativeNotificationService,
createNativePlayerService,
createNativeRecorderService,
SendbirdUIKitContainerProps
} from "@sendbird/uikit-react-native";
import Clipboard from '@react-native-clipboard/clipboard';
import { CameraRoll } from '@react-native-camera-roll/camera-roll';
import RNFBMessaging from '@react-native-firebase/messaging';
import Video from 'react-native-video';
import * as DocumentPicker from 'react-native-document-picker';
import * as FileAccess from 'react-native-file-access';
import * as ImagePicker from 'react-native-image-picker';
import * as Permissions from 'react-native-permissions';
import * as CreateThumbnail from 'react-native-create-thumbnail';
import * as ImageResizer from '@bam.tech/react-native-image-resizer';
import * as AudioRecorderPlayer from 'react-native-audio-recorder-player';
export const platformServices: SendbirdUIKitContainerProps['platformServices'] = {
clipboard: createNativeClipboardService(Clipboard),
notification: createNativeNotificationService({
messagingModule: RNFBMessaging,
permissionModule: Permissions,
}),
file: createNativeFileService({
imagePickerModule: ImagePicker,
documentPickerModule: DocumentPicker,
permissionModule: Permissions,
fsModule: FileAccess,
mediaLibraryModule: CameraRoll,
}),
media: createNativeMediaService({
VideoComponent: Video,
thumbnailModule: CreateThumbnail,
imageResizerModule: ImageResizer,
}),
player: createNativePlayerService({
audioRecorderModule: AudioRecorderPlayer,
permissionModule: Permissions,
}),
recorder: createNativeRecorderService({
audioRecorderModule: AudioRecorderPlayer,
permissionModule: Permissions,
}),
};
Note: Each interface comes with a set of methods and helper functions. Based on the interface, you can create a new class that includes the corresponding methods and implement them in UIKit. Then, you can use the helper functions to set the interface in the individual modules. To do so, pass the module as a parameter in the helper function.
Some of the features provided by Sendbird UIKit include attaching or saving media files and sending file messages. To learn more about using these features, refer to the get native module permission page.
Wrap your app in the SendbirdUIKitContainer
component to provide the necessary context for UIKit components.
Note: Make sure to place the SendbirdUIKitContainer
component at the top and wrap your app with it, just as shown in the example below.
//App.tsx
import { SendbirdUIKitContainer } from '@sendbird/uikit-react-native';
import { MMKV } from 'react-native-mmkv';
const mmkv = new MMKV();
export default function App() {
return (
<SendbirdUIKitContainer
appId={'APP_ID'} // Replace with your Sendbird application ID
chatOptions={{ localCacheStorage: mmkv }}
platformServices={platformServices}
>
{/* Rest of your app */}
</SendbirdUIKitContainer>
);
};
Install and configure React Navigation to handle screen transitions.
npx expo install @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context
yarn add @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context
npx pod-install
Implement the core chat screens:
GroupChannelListFragment
is the starting point when the app opens)GroupChannelCreateFragment
is used to create a new channel)GroupChannelFragment
is used to display messages and send new messages)// App.tsx
import { useNavigation, useRoute } from '@react-navigation/native';
import {
useSendbirdChat,
createGroupChannelListFragment,
createGroupChannelCreateFragment,
createGroupChannelFragment,
} from '@sendbird/uikit-react-native';
import { useGroupChannel } from '@sendbird/uikit-chat-hooks';
const GroupChannelListFragment = createGroupChannelListFragment();
const GroupChannelCreateFragment = createGroupChannelCreateFragment();
const GroupChannelFragment = createGroupChannelFragment();
const GroupChannelListScreen = () => {
const navigation = useNavigation<any>();
return (
<GroupChannelListFragment
onPressCreateChannel={(channelType) => {
// Navigate to GroupChannelCreate function.
navigation.navigate('GroupChannelCreate', { channelType });
}}
onPressChannel={(channel) => {
// Navigate to GroupChannel function.
navigation.navigate('GroupChannel', { channelUrl: channel.url });
}}
/>
);
};
const GroupChannelCreateScreen = () => {
const navigation = useNavigation<any>();
return (
<GroupChannelCreateFragment
onCreateChannel={async (channel) => {
// Navigate to GroupChannel function.
navigation.replace('GroupChannel', { channelUrl: channel.url });
}}
onPressHeaderLeft={() => {
// Go back to the previous screen.
navigation.goBack();
}}
/>
);
};
const GroupChannelScreen = () => {
const navigation = useNavigation<any>();
const { params } = useRoute<any>();
const { sdk } = useSendbirdChat();
const { channel } = useGroupChannel(sdk, params.channelUrl);
if (!channel) return null;
return (
<GroupChannelFragment
channel={channel}
onChannelDeleted={() => {
// Navigate to GroupChannelList function.
navigation.navigate('GroupChannelList');
}}
onPressHeaderLeft={() => {
// Go back to the previous screen.
navigation.goBack();
}}
onPressHeaderRight={() => {
// Navigate to GroupChannelSettings function.
navigation.navigate('GroupChannelSettings', { channelUrl: params.channelUrl });
}}
/>
);
};
useConnection
hook.//App.tsx
import { Pressable, Text, View } from 'react-native';
import { useConnection } from '@sendbird/uikit-react-native';
const SignInScreen = () => {
const { connect } = useConnection();
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Pressable
style={{
width: 120,
height: 30,
backgroundColor: '#742DDD',
alignItems: 'center',
justifyContent: 'center',
}}
// TODO: Use the ID of a user you've created on the dashboard.
// If there isn't one, specify a unique ID so that a new user can be created with the value.
onPress={() => connect('USER_ID', {accessToken: 'ACCESS_TOKEN'})}
>
<Text>{'Sign in'}</Text>
</Pressable>
</View>
);
};
Note: You can find the user's Access token in the Sendbird Dashboard under your Application > Users > your user > Access token. For this tutorial, you are using the user access token as a way of authentication. For actual implementation, itt is highly recommended to refer to this authentication guide to implement a more secure way of authentication.
Now that you've created the screens, you need to register them. Think of it like creating a menu - we're listing all the screens users can visit and how to get there.
The code below:
//App.tsx
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
const RootStack = createNativeStackNavigator();
const Navigation = () => {
const { currentUser } = useSendbirdChat();
return (
<NavigationContainer>
<RootStack.Navigator screenOptions={{ headerShown: false }}>
{!currentUser ? (
<RootStack.Screen name={'SignIn'} component={SignInScreen} />
) : (
<>
<RootStack.Screen name={'GroupChannelList'} component={GroupChannelListScreen} />
<RootStack.Screen name={'GroupChannelCreate'} component={GroupChannelCreateScreen} />
<RootStack.Screen name={'GroupChannel'} component={GroupChannelScreen} />
</>
)}
</RootStack.Navigator>
</NavigationContainer>
);
};
export default function App() {
return (
<SendbirdUIKitContainer
appId={'APP_ID'}
chatOptions={{ localCacheStorage: mmkv }}
platformServices={platformServices}
>
<Navigation />
</SendbirdUIKitContainer>
);
};
Now let's test the chat interface by sending a message:
npx react-native run-ios # or run-android
npx expo run:ios # or run:android
You've successfully: