How to Build a Real-Time Chat App with React Native and Node.js 💬
Real-time communication is an important part of modern mobile applications.
Messaging apps, customer support systems, collaboration tools, delivery applications, and social platforms all need the ability to exchange information quickly.
A traditional REST API is excellent for many use cases, but real-time applications often need a persistent connection between the client and the server.
One practical architecture is:
```text id="j5m9xk"
React Native
↓
WebSocket
↓
Node.js
↓
Database
In this article, we'll look at the main concepts behind building a real-time chat application.
## REST API vs WebSocket
With a traditional REST API, the client sends a request and waits for a response.
```text id="s6z7qn"
Mobile App
↓
HTTP Request
↓
Server
↓
HTTP Response
↓
Mobile App
This works well for things like:
- Fetching products
- Creating orders
- Updating profiles
- Authentication
But chat applications need something different.
Imagine two users are having a conversation.
User A sends a message:
```text id="7uh0ml"
User A
↓
Server
User B should receive the message immediately.
With WebSockets, the server can maintain an active connection:
```text id="1k6n5f"
User A ←──── WebSocket ────→ Server
↕
WebSocket
↕
User B ←──────────────────────┘
The server can send data to connected clients without waiting for a new HTTP request.
What Is WebSocket?
WebSocket is a communication protocol that provides a persistent, two-way connection between a client and a server.
Unlike traditional HTTP requests, the connection can remain open.
```text id="4g5q1k"
Client
↕
WebSocket Connection
↕
Server
Both sides can send messages.
This makes WebSockets useful for:
* Chat
* Notifications
* Live dashboards
* Multiplayer games
* Collaboration tools
* Real-time tracking
---
# React Native Client
The mobile application can establish a WebSocket connection.
A simplified example:
```javascript id="p3z5q7"
const socket = new WebSocket(
"wss://api.example.com/socket"
);
socket.onopen = () => {
console.log("Connected");
};
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
console.log(message);
};
socket.onerror = (error) => {
console.error(error);
};
Once the connection is established, the application can listen for incoming messages.
Sending a Message
When a user sends a message, the mobile application can send data through the WebSocket.
For example:
```javascript id="r8x3sl"
socket.send(
JSON.stringify({
type: "message",
conversationId: "123",
text: "Hello!"
})
);
The server receives the message and processes it.
A simplified flow:
```text id="m3j4fr"
React Native
↓
WebSocket
↓
Node.js
↓
Validate Message
↓
Save to Database
↓
Send to Recipient
Node.js Backend
Node.js is well suited for applications that maintain many concurrent connections.
A WebSocket server can listen for connections:
```javascript id="n7k2ds"
socketServer.on("connection", (socket) => {
console.log("Client connected");
socket.on("message", (data) => {
const message = JSON.parse(data);
console.log(message);
});
});
In a production application, the server would also handle authentication, validation, persistence, and message delivery.
---
# Authentication
A real chat application needs authentication.
We don't want anonymous users to connect and access private conversations.
A typical flow could be:
```text id="u9r3kl"
Login
↓
Node.js API
↓
Authentication
↓
Access Token
↓
WebSocket Connection
The server can verify the user's identity when establishing the connection.
Then the server knows which user is connected.
For example:
```text id="x2a6pl"
User ID: 123
Connection: WebSocket #ABC
When a message arrives for user `123`, the server knows which connection should receive it.
---
# Conversations
A chat application usually contains multiple conversations.
For example:
```text id="v6f8qw"
User
├── Conversation A
├── Conversation B
└── Conversation C
Each conversation can have multiple messages.
A simplified database structure could look like:
```text id="z5k1nm"
Users
↓
Conversations
↓
Messages
A message might contain:
```json id="f8j3mz"
{
"id": "msg_123",
"conversationId": "conversation_1",
"senderId": "user_123",
"text": "Hello!",
"createdAt": "2026-08-14T10:00:00Z"
}
Storing Messages
WebSocket provides real-time communication, but messages should usually be stored in a database.
For example:
```text id="r5j7nx"
User sends message
↓
WebSocket
↓
Node.js
↓
Database
↓
Recipient
Why save the message?
Because users may:
* Close the application
* Lose their internet connection
* Change devices
* Open the conversation later
The database provides persistent storage.
---
# Online Status
Real-time connections can also be used to track online users.
For example:
```text id="v8q3ma"
User connects
↓
Online
User disconnects
↓
Offline
The backend can maintain a mapping:
```text id="t3f7ny"
User 101 → Connection A
User 205 → Connection B
User 309 → Connection C
When a connection closes, the server can update the user's status.
---
# Typing Indicators
A small feature like:
> User is typing...
can also use WebSockets.
The flow could be:
```text id="n4g8pv"
User A starts typing
↓
WebSocket
↓
Server
↓
User B
↓
"User A is typing..."
The actual text doesn't need to be sent until the user presses the send button.
Message Delivery
Real-time doesn't necessarily mean the message is permanently delivered.
Networks can fail.
A more reliable system can track message states:
```text id="s2m7qx"
Sending
↓
Sent
↓
Delivered
↓
Read
This allows the application to provide familiar messaging features.
---
# Handling Reconnection
Mobile networks are unreliable.
A user might:
* Enter a tunnel
* Switch from Wi-Fi to mobile data
* Lose signal
* Lock their phone
* Put the application in the background
The WebSocket connection can therefore disappear.
The application should detect disconnections and attempt to reconnect.
```text id="a7x2pz"
Connected
↓
Disconnected
↓
Reconnect
↓
Connected
A production application should also avoid reconnecting too aggressively.
Push Notifications
What happens when the recipient isn't connected?
The backend can use push notifications.
For example:
```text id="k3n8wp"
Sender
↓
Node.js
↓
Recipient Offline?
↓
Push Notification
↓
iOS / Android
When the user opens the application, the app can then synchronize the latest messages from the backend.
This gives us two complementary systems:
```text id="m8f2vx"
WebSocket
→ Real-time communication
Push Notifications
→ Notify users when offline
Scaling the WebSocket Server
One WebSocket server may be enough for a small application.
But imagine thousands or millions of concurrent connections.
You may eventually need multiple server instances:
```text id="b4y7nm"
Load Balancer
/ | \
↓ ↓ ↓
Server Server Server
\ | /
└── Redis ──┘
A shared system such as Redis can help coordinate information between server instances.
For example:
```text id="d6k2rx"
Server A
↓
Redis
↓
Server B
↓
User
This becomes important when a sender and recipient are connected to different backend instances.
Security
Real-time systems also need strong security.
Important areas include:
- Authentication
- Authorization
- Input validation
- Rate limiting
- Secure WebSocket connections
- Message validation
- Access control
Use:
```text id="w4c9hs"
wss://
instead of an unencrypted WebSocket connection when running in production.
---
# Complete Architecture
Putting the pieces together:
```text id="q8m2kd"
React Native
Mobile App
│
│
REST API / WebSocket
│
↓
Node.js Backend
│
┌──────────┼──────────┐
↓ ↓ ↓
Redis Database Push Service
│
↓
WebSocket Servers
│
↓
Other Connected Users
REST APIs can handle normal application operations, while WebSockets handle real-time communication.
This combination is often more practical than trying to use only one communication method.
When Should You Use WebSockets?
WebSockets are useful when information needs to update quickly.
Good examples include:
- Chat applications
- Live notifications
- Real-time dashboards
- Delivery tracking
- Multiplayer applications
- Collaborative editors
- Trading interfaces
For simple CRUD applications, however, a normal REST API may be completely sufficient.
Not every application needs WebSockets.
Final Thoughts
Building a real-time chat application requires more than opening a WebSocket connection.
A production-ready system needs:
- Authentication
- Message persistence
- Reliable delivery
- Reconnection
- Push notifications
- Security
- Database design
- Monitoring
- Scalability
A practical architecture can combine:
React Native + WebSocket + Node.js + Database + Push Notifications
The important part is not simply making messages appear instantly.
It's building a system that remains reliable when users disconnect, reconnect, switch devices, and communicate at scale.
Real-time UX starts with reliable architecture. 🚀
Top comments (0)