Building a Chat App with React Native (Part 4): Creating Chat UI Screens

This is the fourth part of a series on building a real-time chat application with React Native and Firebase. In [Part 3](https://Aland Baban/blog/chat-app-with-react-native-part-3), Firestore integration was completed, enabling the creation and storage of authenticated chat rooms.
This part focuses on building the chat room screen where users can send and receive messages. The implementation will utilize the react-native-gifted-chat library, a popular open-source solution that provides a feature-rich, customizable chat UI out of the box, significantly reducing development time.
First, install the library in your project:
yarn add react-native-gifted-chat
Adding the Room Screen
Create a new file src/screens/RoomScreen.js to display the messages for a specific chat room. This component will use the GiftedChat component to render the chat interface.
We will initialize it with some mock data to visualize the UI.
// src/screens/RoomScreen.js
import React, { useState } from 'react';
import { GiftedChat } from 'react-native-gifted-chat';
export default function RoomScreen() {
const [messages, setMessages] = useState([
// System message
{
_id: 0,
text: 'New room created.',
createdAt: new Date().getTime(),
system: true
},
// Example chat message
{
_id: 1,
text: 'Hello!',
createdAt: new Date().getTime(),
user: {
_id: 2,
name: 'Test User'
}
}
]);
// Helper method to handle sending messages
function handleSend(newMessage = []) {
setMessages(GiftedChat.append(messages, newMessage));
}
return (
<GiftedChat
messages={messages}
onSend={newMessage => handleSend(newMessage)}
user={{ _id: 1 }}
/>
);
}
Integrating the Room Screen into Navigation
Next, add the RoomScreen to the stack navigator in src/navigation/HomeStack.js. This will allow users to navigate from the list of chat rooms on the home screen to an individual room.
// src/navigation/HomeStack.js
import RoomScreen from '../screens/RoomScreen';
// ...
function ChatApp() {
return (
<ChatAppStack.Navigator
// ... screenOptions
>
<ChatAppStack.Screen name="Home" component={HomeScreen} /* ... options */ />
<ChatAppStack.Screen
name="Room"
component={RoomScreen}
options={({ route }) => ({
title: route.params.thread.name
})}
/>
</ChatAppStack.Navigator>
);
}
The title for the RoomScreen is dynamically set using the name property of the thread object passed via route params.
In src/screens/HomeScreen.js, wrap each item in the FlatList with a TouchableOpacity to handle navigation. The onPress handler will navigate to the Room screen, passing the item object (the thread) as a parameter.
// src/screens/HomeScreen.js
// ...
<FlatList
data={threads}
keyExtractor={item => item._id}
ItemSeparatorComponent={() => <Divider />}
renderItem={({ item }) => (
<TouchableOpacity
onPress={() => navigation.navigate('Room', { thread: item })}
>
<List.Item
title={item.name}
description="Item description"
// ... other props
/>
</TouchableOpacity>
)}
/>
This setup allows users to tap on a chat room from the list and be taken to the corresponding message screen.

Customizing the Chat UI with Gifted Chat
react-native-gifted-chat offers extensive customization through props. Let's explore a few common customizations.
Customizing the Chat Bubble
To change the appearance of the message bubbles, you can use the renderBubble prop. This prop accepts a function that returns a customized Bubble component from the library.
In RoomScreen.js, create a renderBubble function to change the background color of the user's messages.
import { GiftedChat, Bubble } from 'react-native-gifted-chat';
// ...
function renderBubble(props) {
return (
<Bubble
{...props}
wrapperStyle={{
right: {
backgroundColor: '#6646ee' // Custom color for sent messages
}
}}
textStyle={{
right: {
color: '#fff'
}
}}
/>
);
}
// Then in the JSX
<GiftedChat
// ... other props
renderBubble={renderBubble}
/>
Customizing the Send Button
The send button can be customized using the renderSend prop. This allows you to replace the default button with a custom component, such as an icon button.
import { Send } from 'react-native-gifted-chat';
import { IconButton } from 'react-native-paper';
// ...
function renderSend(props) {
return (
<Send {...props}>
<View style={styles.sendingContainer}>
<IconButton icon="send-circle" size={32} color="#6646ee" />
</View>
</Send>
);
}
// ...
<GiftedChat
// ...
alwaysShowSend
renderSend={renderSend}
/>
The alwaysShowSend prop ensures the send button is always visible, even when the input is empty.
Adding a "Scroll to Bottom" Button
For long chat histories, a "scroll to bottom" button is essential for usability. GiftedChat provides the scrollToBottom prop to enable this functionality and scrollToBottomComponent to customize its appearance.
function scrollToBottomComponent() {
return (
<View style={styles.bottomComponentContainer}>
<IconButton icon="chevron-double-down" size={36} color="#6646ee" />
</View>
);
}
// ...
<GiftedChat
// ...
scrollToBottom
scrollToBottomComponent={scrollToBottomComponent}
/>
Adding a Loading Indicator
To improve the user experience while messages are being fetched, you can provide a loading indicator using the renderLoading prop.
import { ActivityIndicator } from 'react-native';
// ...
function renderLoading() {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#6646ee" />
</View>
);
}
// ...
<GiftedChat
// ...
renderLoading={renderLoading}
/>
This will display a spinner while the initial messages for the room are being loaded.
Next Steps
The next part of this series will focus on integrating Firestore to enable real-time messaging. This will involve:
- Retrieving the current room's ID using React Navigation.
- Associating messages with the current user from the
AuthContext. - Storing and fetching messages from Firestore in real-time.
- Displaying the latest message for each chat room on the home screen.
The complete source code for this project is available at this GitHub repository.