Rabikant

Posted on March 9th

Build a Real-Time Chat App in React Native with PieSocket

"Let's Learn How To Build a Real-Time Chat App in React Native with PieSocket"

If you've ever wanted to build a real-time chat app in React Native, this guide is for you! We'll walk through how to build a clean and modern chat interface that supports real-time messaging using PieSocket — a scalable and developer-friendly WebSocket solution

By the end of this tutorial, you’ll have a fully functional chat app that works in real time with messages aligned neatly for both sender and receiver.

Prerequisites

Make sure you have the following set up:

  • Node.js and npm/yarn installed
  • React Native CLI or Expo
  • A PieSocket account (create one for free at PieSocket)
  • React Native development environment (iOS/Android)

Step 1: Initialize Your React Native App

npx react-native init ChatApp
cd ChatApp

Install dependencies:

npm install piesocket-js react-native-get-random-values

Note: react-native-get-random-values is used to generate unique user IDs.

State and References Initialization

const [message, setMessage] = useState('');
const [messages, setMessages] = useState([]);
const [connected, setConnected] = useState(false);
const piesocketRef = useRef(null);
const usernameRef = useRef('User_' + Math.floor(Math.random() * 1000));

What this does:

  • message: The current message the user is typing.
  • messages: An array of all chat messages.
  • connected: Boolean to track if connection to PieSocket was successful.
  • piesocketRef: A ref to store the active PieSocket channel instance.
  • usernameRef: Generates and stores a random username like User_123.

useEffect Hook to Setup PieSocket

useEffect(() => {
    const piesocket = new PieSocket({
        apiKey: '',
        clusterId: '',
        notifySelf: true,
        presence: true,
        userId: usernameRef.current,
    });

What this does:

  • Initializes the PieSocket connection with the given credentials (apiKey, clusterId, etc.).
  • notifySelf: true: Allows this client to receive its own published messages.
  • presence: true: Enables presence features (tracking who's online).
  • userId: Tells PieSocket which user is currently connected.

Note: Replace '' and '' with actual apiKey and clusterId.

Subscribing to the Chat Room Channel

  piesocket.subscribe('chat-room').then((channel) => {
        piesocketRef.current = channel;
        setConnected(true);

What this does:

  • Subscribes the client to a channel named "chat-room".
  • Saves the channel reference in piesocketRef for future use.
  • Sets connected = true so the app knows PieSocket is ready.

Listening to New Messages

        channel.listen('new_message', (data) => {
            setMessages((prev) => [
                {
                    id: Date.now().toString() + Math.random(),
                    text: data.text,
                    user: data.sender,
                    createdAt: new Date(),
                },
                ...prev,
            ]);
        });

What this does:

  • Listens for an event named new_message.
  • When a new message arrives, it's added to the top of the messages array.
  • Each message has:
    • id: Unique ID.
    • text: Message content.
    • user: Sender's username.
    • createdAt: Timestamp for display.

Listening for New Members Joining

        channel.listen('system:member_joined', (data) => {
            console.log(`${data.member.user} joined the chat`);
        });

What this does:

  • Listens for presence events (someone joining).
  • Logs a message like: User_456 joined the chat.

Cleanup on Unmount

    return () => {
        if (piesocketRef.current) {
            piesocketRef.current.disconnect();
        }
    };
}, []);

What this does:

  • On component unmount, it disconnects from the PieSocket channel to avoid memory leaks or open socket connections.

Sending a Message

const sendMessage = () => {
    if (!message.trim() || !piesocketRef.current) return;

    piesocketRef.current.publish('new_message', {
        sender: usernameRef.current,
        text: message,
    });

    setMessage('');
};

What this does:

  • Checks if message is empty:

    !message.trim() ensures blank messages aren't sent.

  • Checks if PieSocket is connected:

    piesocketRef.current must exist to send a message.

  • Publishes the message to the channel:

    Using piesocketRef.current.publish(), it sends:

    • sender: the current user (User_123)
    • text: the message string
  • Clears the input field after sending with setMessage('').

Rendering Each Chat Message

const renderItem = ({ item }) => {
    const isMyMessage = item.user === usernameRef.current;

    return (
        <Viewstyle={[
                styles.messageRow,
                isMyMessage ? styles.messageRowRight : styles.messageRowLeft,
            ]}
        >
            <Viewstyle={[
                    styles.messageBubble,
                    isMyMessage ? styles.myMessage : styles.otherMessage,
                ]}
            >
                <Text style={styles.username}>
                    {isMyMessage ? 'You' : item.user}
                </Text>
                <Text style={styles.messageText}>{item.text}</Text>
                <Text style={styles.timestamp}>
                    {item.createdAt.toLocaleTimeString([], {
                        hour: '2-digit',
                        minute: '2-digit',
                    })}
                </Text>
            </View>
        </View>
    );
};

What this does:

  • isMyMessage: Checks if the message is from the current user.
  • Layout Alignment:
    • messageRowRight: Aligns messages you sent to the right.
    • messageRowLeft: Aligns messages from others to the left.
  • Bubble Styling:
    • myMessage: Greenish background bubble (sent by you).
    • otherMessage: Grey background (sent by others).
  • Username Display:
    • "You" if it’s your own message.
    • Otherwise shows the sender’s username.
  • Message Content:
    • item.text: The actual message.
    • item.createdAt: Time the message was created, shown in HH:MM format.

Handle Keyboard Gracefully

<KeyboardAvoidingView
    behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
    style={styles.container}
>

What's happening:

  • This view helps prevent the keyboard from covering inputs.
  • It behaves differently on iOS vs Android:
    • iOS: adds padding when keyboard appears.
    • Android: adjusts height.
  • It wraps the entire chat layout so the input box stays visible when typing.

Displaying Chat Messages

<FlatList
    data={messages}
    renderItem={renderItem}
    keyExtractor={(item) => item.id}
    inverted
    contentContainerStyle={{ padding: 10 }}
/>

What each prop does:

  • data={messages}: The list of messages to show.
  • renderItem={renderItem}: A function to render each message (we covered this earlier).
  • keyExtractor={(item) => item.id}: Ensures each message has a unique key (for performance).
  • inverted: Flips the list vertically so new messages appear at the bottom.
  • contentContainerStyle: Adds padding inside the chat area.

Input Section — Typing and Sending Messages

<View style={styles.inputContainer}>
    <TextInputstyle={styles.input}
        placeholder="Type a message..."
        placeholderTextColor="#888"
        value={message}
        onChangeText={setMessage}
    />
    <TouchableOpacitystyle={[
            styles.sendButton,
            { backgroundColor: connected ? '#4CAF50' : '#aaa' },
        ]}
        onPress={sendMessage}
        disabled={!connected}
    >
        <Text style={styles.sendButtonText}>Send</Text>
    </TouchableOpacity>
</View>

What's going on here:

  • TextInput: The message box where users type text.
    • Bound to message via value.
    • onChangeText updates state.
  • TouchableOpacity: The Send button.
    • Green (#4CAF50) if connected, grey if not.
    • disabled={!connected} disables the button if not connected to PieSocket.
    • onPress={sendMessage} triggers the send function we explained earlier.

Main Container Layout

container: {
    flex: 1,
    backgroundColor: '#f4f4f4',
},
  • flex: 1 → Fills the entire screen vertically.
  • backgroundColor: '#f4f4f4' → Light grey background for the chat screen.

Message Row Placement

messageRow: {
    flexDirection: 'row',
    marginVertical: 5,
},
messageRowLeft: {
    justifyContent: 'flex-start',
},
messageRowRight: {
    justifyContent: 'flex-end',
},
  • messageRow sets messages in a horizontal row.
  • messageRowLeft and messageRowRight determine if the message aligns left (other users) or right (your messages).

Message Bubble Styles

messageBubble: {
    padding: 10,
    borderRadius: 10,
    maxWidth: '75%',
},
myMessage: {
    backgroundColor: '#dcf8c6',
    alignSelf: 'flex-end',
},
otherMessage: {
    backgroundColor: '#e5e5ea',
    alignSelf: 'flex-start',
},
  • messageBubble → Common styling for all bubbles (padding, rounded corners, max width).
  • myMessage → Light green (#dcf8c6), aligned to the right.
  • otherMessage → Light grey (#e5e5ea), aligned to the left.

Text Styles Inside the Bubbles

username: {
    fontWeight: 'bold',
    fontSize: 12,
    color: '#555',
    marginBottom: 2,
},
messageText: {
    fontSize: 16,
    color: '#333',
},
timestamp: {
    fontSize: 10,
    color: '#777',
    marginTop: 4,
    textAlign: 'right',
},
  • username → Small bold name above the message.
  • messageText → The main chat message text.
  • timestamp → Shows the time under the message, aligned to the right.

Input Section Styles (Text box + Send Button)

inputContainer: {
    flexDirection: 'row',
    padding: 10,
    borderTopWidth: 1,
    borderColor: '#ddd',
    backgroundColor: '#fff',
    alignItems: 'center',
},
  • Organizes the input row: text field + send button.
  • Adds padding and border to separate it from the chat.
input: {
    flex: 1,
    backgroundColor: '#f1f1f1',
    borderRadius: 25,
    paddingHorizontal: 15,
    paddingVertical: 10,
    fontSize: 16,
},
  • Rounded, light-colored input box.
  • Takes most of the row (flex: 1) for typing.
sendButton: {
    paddingVertical: 10,
    paddingHorizontal: 20,
    borderRadius: 25,
    marginLeft: 10,
},
sendButtonText: {
    color: '#fff',
    fontWeight: 'bold',
    fontSize: 16,
},
  • Circular-style send button with bold white text

Final Result

You now have a working chat app with:

  • Real-time messages powered by PieSocket
  • Smooth UI with message alignment
  • Random usernames and timestamps

Complete Code

The project is available on our GitHub : https://github.com/piehostHQ/react-native-chat

Comments

Leave a comment.

Share your thoughts or ask a question to be added in the loop.