Realtime · from our own builds
Socket.IO integration for chat, presence, typing and live driver location
Integrating Socket.IO takes authenticated handshakes, a personal room for every user so each screen and device hears its events, a Redis adapter before you run a second process, and a plan for what a phone misses while it is disconnected. We built it into three apps: chat with presence and typing in Trainr and LetSlic, and live driver location in Pakka. Most of our bugs were not in Socket.IO itself but in rooms nobody had joined, listeners removed by mistake and reconnects nobody handled.
Updated 20 October 2026 · checked against our code and the vendors' documentation
Where have we built this?
- TrainrOne-to-one and group chat with presence kept in Redis, typing indicators and read receipts, in an app live on Google Play.
- LetSlicGroup chat with typing, reactions, read receipts and moderation, served by one worker per CPU joined through the Socket.IO Redis adapter.
- PakkaDriver location relayed to the booking room only while a trip is under way, plus trip requests and status changes for drivers and customers.
What are the numbers and limits to know?
- Heartbeat defaults (Socket.IO v4)
- pingInterval 25,000 ms and pingTimeout 20,000 ms; React Native may warn when the two add up to more than 60 secondssource, checked 20 October 2026
- Largest single message (maxHttpBufferSize)
- 1e6 bytes (1 MB) by default; a bigger message closes the socketsource, checked 20 October 2026
- Default transports
- ["polling", "websocket"]: the connection starts on HTTP long-polling unless you change itsource, checked 20 October 2026
- Running several servers
- Every request of a session must reach the process that created it (sticky sessions, or disable HTTP long-polling), and an adapter such as Redis must pass events between serverssource, checked 20 October 2026
- Refused handshakes
- When the server denies a connection in a middleware, the client does not reconnect automatically; socket.connect() must be calledsource, checked 20 October 2026
- Connection state recovery
- Added in v4.6.0, off by default, and not supported by the Redis pub/sub adapter (the Redis Streams adapter supports it)source, checked 20 October 2026
- Events emitted while offline
- Buffered on the client by default and sent on reconnect; volatile emits are dropped insteadsource, checked 20 October 2026
- Pakka location updates accepted per driver
- At most one every 5 seconds, coordinates range-checked
How do you integrate Socket.IO, step by step?
- 1
Authenticate in the handshake
Trainr and Pakka send the JWT access token in the handshake's auth field and verify it before the connection is accepted. A missing or bad token is refused, so no event handler ever runs for an unknown user.
- 2
Give every user a personal room
On connect each socket joins user_<id>. Trainr sends chat-list updates and unread counts to that room, so every screen and every device the user has open hears them, not only the open conversation.
- 3
Check ownership before joining a room
Pakka allows only two room prefixes and joins a booking room only for the customer who owns the booking or the driver assigned to it. A guessed room name gets nothing.
- 4
Add the Redis adapter before a second process
LetSlic runs one worker per CPU and joins them with @socket.io/redis-adapter. Trainr stays on one process until sticky sessions sit in front of it, and its app connects on WebSocket only, so there is no polling handshake to route.
- 5
Keep presence in Redis with an expiry
Trainr adds the user to an online set on connect and removes them on disconnect, and writes a presence record with the last-seen time that expires after 300 seconds unless a heartbeat every 30 seconds renews it.
- 6
Rate-limit the chatty events
Typing and location fire constantly. With rate limiting switched on, Trainr's limiter allows one typing_start per user a second; Pakka accepts one location update per driver every 5 seconds and relays it only while the trip is on.
- 7
Resend what a disconnected phone missed
Pakka replays open trip requests to a driver the moment the socket reconnects and sends them by push as well. LetSlic refetches chats and unread counts whenever the app returns to the foreground.
- 8
Keep the source of truth outside the socket
Pakka writes each accepted position to the driver's record in MongoDB. If the customer's socket fails for 30 seconds, the tracking screen falls back to polling every 15 seconds and keeps working.
What went wrong, and how was it fixed?
A chat list that never heard about new messages
Trainr emitted new_message only to the chat room. A user looking at the chat list had not joined that room, so the list stayed stale until they opened the conversation.
Fix: Fixed on 15 February 2026: the server also emits to each participant's personal room, with the unread count alongside.
Workers that could not see each other
With a worker per CPU and no shared state, LetSlic showed connection counts of zero, lost messages and flipped people between online and offline, depending on which worker a socket had landed on. After the adapter went in, read receipts still went missing when the sender was connected to another worker, because the code looked sockets up in a local map.
Fix: The Redis adapter went in on 28 January 2026. On 9 May 2026 read receipts and new messages moved to user-room emits, which the adapter routes to whichever worker holds the socket.
Cluster mode without sticky sessions
Socket.IO starts on HTTP long-polling, and a polling handshake split across workers fails unless requests stick to one process. Trainr had no sticky load balancer in front of its workers.
Fix: Cluster mode became opt-in on 25 April 2026, single process by default, and the app connects with transports ['websocket'] and upgrade off.
.off(event) without a handler removes every listener
Trainr's chat-list screens cleaned up with socket.off(event) and no callback, which removed every listener for that event, including other screens'.
Fix: Fixed in Trainr by keeping handler references and passing them to .off().
A trip request sent into an empty room
A tester's driver socket dropped for two minutes. The trip request went into a room with nobody in it, the socket came back with 25 seconds left, the app re-read only the profile, and the request expired without an accept ever reaching the server.
Fix: Fixed on 16 September 2026: the server stores the promised earnings with each offer, resends open requests when a driver's socket connects, and the same request now also arrives as a push notification.
Every deploy silently took drivers offline
After a backend restart the server refused sockets holding an access token older than 15 minutes, and Socket.IO does not retry a refusal from the auth middleware. No location reached the server, the driver was marked offline five minutes later, and the warning travelled on the same dead socket.
Fix: Found on 11 September 2026 and fixed: a refused connection refreshes the session through the same single-flight refresh the HTTP client uses, then reconnects with a backoff capped at 60 seconds.
Typing that re-rendered a 1,500-line screen
In Trainr every keystroke from the other person re-rendered the whole chat screen, message list included, and the chat list re-rendered on any of twenty-plus socket event types.
Fix: Typing moved into its own self-subscribing component, and the chat list's selector was narrowed to nine fields compared with shallowEqual. It shipped in build 1.0.2.
What should you check before going live?
- Handshake rejects connections without a valid access token
- Each user joins a personal room; list and badge updates go there
- Room joins check that the user owns the conversation or trip
- Server removes sockets from a room when the chat or trip ends
- Redis adapter in place before a second process
- Sticky sessions, or WebSocket-only clients, behind any cluster
- pingInterval plus pingTimeout at or under 60 s for React Native clients
- Every .off() call passes the handler it is removing
- Presence counts a user's connections before marking them offline
- Missed events resent or refetched on reconnect and on foreground
Questions about Socket.IO
Do you need Redis to run Socket.IO?
Not for a single process. As soon as you run a second process or server, yes: without an adapter each process only knows its own sockets. LetSlic showed zero connections and lost messages until the Redis adapter joined its workers.
Why do chat apps miss new messages on the conversation list?
Usually because the message is sent only to the conversation's room, and a user on the list has not joined it. We fixed this in Trainr by also sending to each participant's personal room, which every screen and device of theirs is in.
How does an app know who is online right now?
Trainr puts a user in an online set in Redis when their socket connects and takes them out on disconnect, alongside a presence record with the last-seen time that expires after 5 minutes unless a 30-second heartbeat renews it.
What happens to a real-time event when the phone is offline?
Socket.IO does not queue it for you on the server, and its connection-recovery feature is off by default and does not work with the Redis pub/sub adapter. Pakka resends open trip requests on reconnect and also sends them as push notifications; LetSlic refetches chats when the app comes back to the foreground.
How often should a driver app send its location?
Pakka's driver app sends after 20 m of movement, at most every 10 seconds, and a foreground service keeps it going when the app is in the background. The server accepts one update per driver every 5 seconds and relays it to the customer only during a trip.
Should a React Native app connect on WebSocket only?
Trainr does, with upgrade turned off, because it removes the polling handshake that needs sticky sessions behind several workers. The Socket.IO docs note the trade-off: clients on networks that block WebSockets will not connect.
Need Socket.IO in your app?
We have built it in production code, including the parts that went wrong. Tell us what your app needs and we'll say what it takes.
Ask about this integrationRelated guides: Firebase Cloud Messaging · Google Maps Directions API. Built for: marketplace apps, fitness apps, logistics apps.