Skip to content
Flows
App Builder Generated Intermediate · 60-120 minutes

Add Real-Time Presence Indicators to a Chat App with WebSockets

A step-by-step flow for implementing real-time online, offline, and typing presence in a chat app using WebSockets, including backend presence tracking, frontend updates, reconnect handling, and verification.

Start Route · 10 steps

The route

10 steps to Done

  1. 01

    Inspect the existing chat architecture and choose presence data flow

    Establish where WebSocket connections, user identity, and chat participants already exist so presence can be added without breaking the app.

    Preview prompt + verify gate ▾

    Inspect the current codebase and summarize the existing architecture for auth, chat conversations, messages, frontend state, and any real-time transport. Identify where the authenticated user ID becomes available, where chat participants are loaded, whether WebSockets or Socket.IO already exist, and where presence state should live on both backend and frontend. Produce a concrete implementation plan with files to edit, server events to add, client listeners to add, and how presence should be scoped to relevant conversations or participants.

    • Identified the backend entry point for socket connections
    • Confirmed how authenticated user identity is attached to requests or sockets
    • Found the frontend chat screen or conversation header where presence will display
    • Documented whether conversation participants are already known on client and server
    • Listed exact files or modules to modify
  2. 02

    Define the presence event contract and server-side state model

    Create a reliable event schema and in-memory or persistent tracking model before implementation.

    Preview prompt + verify gate ▾

    Create a concrete event and state design for real-time presence in this app. Define payloads for presence:sync, presence:user-online, presence:user-offline, presence:last-seen, typing:start, typing:stop, and any heartbeat or reconnect events if needed. Define the backend tracking model for active connections per user, including support for multiple tabs or devices, and describe how last seen should be stored or derived when the final connection closes.

    • Event names are defined consistently for server and client
    • Payloads include user ID and conversation ID where relevant
    • Multiple active sockets per user are accounted for
    • Offline transition is tied to final disconnect only
    • Typing events are scoped to a conversation
  3. 03

    Implement authenticated WebSocket connection handling on the server

    Ensure the backend can identify users on socket connect and maintain a real presence registry.

    Preview prompt + verify gate ▾

    Edit the real backend socket server code to authenticate each incoming socket using the app's existing auth mechanism. On successful connection, associate the socket with the authenticated user ID, store the socket in a per-user active connection registry, and expose helper functions to increment, decrement, and check active connection counts. On disconnect, remove only that socket and mark the user offline only if they have no remaining active sockets. Also record or update last seen when the final socket disconnects.

    • Each socket resolves to a real authenticated user ID
    • Active sockets are tracked per user
    • Disconnect removes only the current socket connection
    • User stays online if another socket remains active
    • Last seen is updated when the final socket disconnects
  4. 04

    Broadcast presence changes to relevant chat participants

    Send online and offline updates only to users who should see them.

    Preview prompt + verify gate ▾

    Modify the backend so that when a user's first active socket connects, the server emits a presence online event to the users who are allowed to see that user's chat presence. When the user's final socket disconnects, emit a presence offline or last seen event to the same relevant audience. Use the existing conversation membership or participant lookup logic rather than broadcasting globally unless that is how the app is already designed.

    • Online event is emitted only when the first socket connects
    • Offline event is emitted only when the final socket disconnects
    • Recipients are derived from real conversation or participant data
    • Other connected clients receive the event without refresh
    • Current user does not need to manually poll for updates
  5. 05

    Add initial presence sync and frontend presence state management

    Ensure the UI has the correct presence map when the chat screen first loads and stays updated from socket events.

    Preview prompt + verify gate ▾

    Add a presence store in the frontend using the app's existing state pattern. On chat screen load or socket connection, request or receive initial presence data for the relevant conversation participants. Store online status and last seen by user ID, subscribe to presence online and offline events, and update the UI state immediately when events arrive. Make sure event listeners are cleaned up correctly on unmount or conversation changes.

    • Presence state is keyed by user ID
    • Initial presence data is loaded on screen open or socket connect
    • Online and offline events update the store immediately
    • Last seen is stored for offline users if supported
    • Socket listeners are cleaned up to avoid duplicate handlers
  6. 06

    Render presence indicators in the chat UI

    Display presence in conversation headers, user lists, or message panes using the live presence state.

    Preview prompt + verify gate ▾

    Modify the real chat components to display presence for the other participant or participants in each conversation. Use the presence store to show an online label or indicator when a user is connected, and show offline or last seen when they are not. Make sure group chats and one-to-one chats are handled sensibly based on the current UI design, and do not break existing message rendering or conversation layout.

    • Presence indicator appears in the real chat UI
    • Indicator updates when another client connects or disconnects
    • Offline state or last seen renders correctly
    • UI works for the app's conversation types
    • Existing chat layout and message rendering still function
  7. 07

    Implement conversation-scoped typing indicators

    Add real-time typing state that appears only in the relevant conversation and clears correctly.

    Preview prompt + verify gate ▾

    Implement typing:start and typing:stop events in the real app. Emit typing:start from the client when the user begins typing in a conversation, debounce repeated emits, and emit typing:stop after a short idle timeout or when the message is sent. On the server, forward typing events only to other participants in that conversation. On the frontend, show a typing indicator only in the active relevant conversation and clear it on stop, send, conversation switch, or disconnect.

    • Typing events include conversation context
    • Typing indicator is shown only to other participants
    • Typing state clears after idle timeout
    • Typing state clears when a message is sent
    • Typing does not appear in unrelated conversations
  8. 08

    Handle reconnects, stale sockets, and edge cases

    Make presence resilient during refreshes, temporary network drops, and multi-tab usage.

    Preview prompt + verify gate ▾

    Improve the implementation to handle temporary socket disconnects, reconnects, browser refreshes, and multiple tabs or devices. Ensure the client resubscribes and requests initial presence sync after reconnect, the server restores connection tracking correctly, stale typing states are cleared on disconnect, and users remain online if at least one socket stays active. Add any minimal heartbeat or timeout logic only if the existing socket library and app architecture require it.

    • Closing one of multiple tabs does not mark user offline
    • Reconnect restores presence updates without manual refresh
    • Initial sync runs again after reconnect
    • Typing state does not remain stuck after disconnect
    • Temporary network loss does not permanently desync presence
  9. 09

    Test with two or more real clients and instrument debug output

    Verify that presence is actually driven by backend socket events across separate clients.

    Preview prompt + verify gate ▾

    Instrument the backend and frontend with temporary logs for socket connect, disconnect, online broadcast, offline broadcast, typing:start, typing:stop, and initial presence sync. Then run a manual test plan using at least two separate authenticated users in separate browsers or browser profiles. Validate first connect, final disconnect, multi-tab behavior, typing start and stop, message send clearing typing, and reconnect after brief network interruption.

    • Tested with at least two distinct authenticated users
    • Verified live online transition with no refresh
    • Verified offline or last seen on final disconnect only
    • Verified typing appears and clears correctly
    • Verified multi-tab and reconnect scenarios with logs
  10. 10

    Fix discovered issues and clean up implementation details

    Resolve mismatches, remove temporary hacks, and leave a maintainable presence system.

    Preview prompt + verify gate ▾

    Review the manual test results and patch all observed issues in the real app. Fix any duplicated listeners, stale typing state, incorrect offline transitions, missing initial sync, incorrect participant targeting, or UI state mismatches. Remove noisy temporary debug logs, keep any lightweight useful diagnostics, and make sure the final implementation is integrated cleanly with the existing codebase and naming conventions.

    • No duplicate socket listeners remain
    • Presence transitions match real connection state
    • Typing state clears in all tested paths
    • Initial sync and reconnect behavior are stable
    • Temporary debug noise is removed or minimized