Skip to main content
ChatProvider handles all real-time communication automatically. This guide explains what happens behind the scenes and which hooks you use to expose real-time state in your UI.

How Real-time Works

ChatProvider opens a single Socket.io connection for the authenticated user. All components using chat hooks share this one connection. The provider listens for server events and updates Redux state, so any component reading from the hooks will re-render with up-to-date data.

Socket Events Handled Automatically

EventWhat the SDK does
message:createdAppends message to the conversation’s store; increments unread count if the conversation is not currently active
message:updatedUpdates the message’s content in the store
message:deletedMarks the message as soft-deleted in the store
message:removedSets moderationStatus: "removed" on the message
message:reactionUpdates reactionCounts and userReactions on the message
thread:reply_countUpdates threadReplyCount on the parent message
typing:startAdds the user to the typing list for that conversation
typing:stopRemoves the user from the typing list
conversation:updatedMerges the updated conversation fields into the store

Connection State

Use useChatSocket to read whether the socket is connected:
import { useChatSocket } from "@replyke/react-js";

function ConnectionStatus() {
  const { connected } = useChatSocket();
  return <span>{connected ? "Connected" : "Reconnecting..."}</span>;
}
See useChatSocket.

Unread Counts

ChatProvider fetches unread counts on mount and keeps them in sync via socket events.
import { useTotalUnreadCount, useUnreadConversationCount } from "@replyke/react-js";

function ChatBadge() {
  const total = useTotalUnreadCount();
  const withUnread = useUnreadConversationCount();

  return (
    <div>
      <span>{total} unread messages</span>
      <span>{withUnread} conversations with unread messages</span>
    </div>
  );
}
When a new message arrives from a conversation not yet loaded in the conversation list (e.g. paginated out), only useTotalUnreadCount is bumped. useUnreadConversationCount re-syncs on the next ChatProvider mount.

Read Receipts

Mark a conversation as read by calling useMarkConversationAsRead. This clears the unread count in Redux immediately (optimistic) and sends the mark-as-read request to the server.
import { useMarkConversationAsRead } from "@replyke/react-js";

function ConversationView({ conversationId, messages }) {
  const mark = useMarkConversationAsRead({ conversationId });

  // Mark as read when the latest message becomes visible
  useEffect(() => {
    const lastMessage = messages[messages.length - 1];
    if (lastMessage) {
      mark({ messageId: lastMessage.id });
    }
  }, [messages]);
}
See useMarkConversationAsRead.

Typing Indicators

useTypingIndicator manages both sending typing events to other users and receiving typing events from them.
import { useTypingIndicator } from "@replyke/react-js";

function MessageInput({ conversationId }) {
  const { typingUsers, startTyping, stopTyping } = useTypingIndicator({
    conversationId,
  });

  return (
    <div>
      {typingUsers.length > 0 && (
        <p>{typingUsers.length} person(s) typing...</p>
      )}
      <input
        onChange={() => startTyping()}
        onBlur={stopTyping}
      />
    </div>
  );
}

Sender Protocol

  • Call startTyping() on each keystroke. The hook throttles the emit to every 2 seconds internally — you do not need to debounce.
  • Call stopTyping() when the user sends the message, clears the input, or blurs the field.
  • The hook emits typing:stop automatically on unmount if the user was typing.

Receiver Side

typingUsers is an array of user IDs currently typing in this conversation. The current user is excluded. Each typing user is auto-removed after 5 seconds of inactivity (no keep-alive event from the server). See useTypingIndicator.

Reporting Messages

Users can report messages for moderation review:
import { useReportMessage } from "@replyke/react-js";

function MessageMenu({ conversationId, messageId }) {
  const report = useReportMessage();

  return (
    <button onClick={() => report({ conversationId, messageId, reason: "spam" })}>
      Report
    </button>
  );
}
See useReportMessage.