Client SDK Reference
Complete reference for sockr-client. Covers the framework-agnostic SocketClient core, all 12 React hooks, and platform-specific notes.
SocketClient
The core client class. Works in any JavaScript environment — browser, Node.js, React Native — with no React dependency.
import { SocketClient } from 'sockr-client';
const client = new SocketClient(config: ClientConfig);
ClientConfig
| Property | Type | Default | Description |
|---|---|---|---|
url | string | required | WebSocket server URL. |
autoConnect | boolean | true | Connect immediately on instantiation. |
reconnection | boolean | true | Enable automatic reconnection. |
reconnectionAttempts | number | 5 | Maximum reconnection attempts before giving up. |
reconnectionDelay | number | 1000 | Base delay in ms. Delay = reconnectionDelay × attempt (linear backoff). |
timeout | number | 20000 | Connection timeout in milliseconds. |
transports | ('websocket'|'polling')[] | ['websocket'] | Allowed Socket.IO transports. Use ['websocket'] for React Native. |
ConnectionState
An enum describing the client's current connection lifecycle state.
| Value | Description |
|---|---|
IDLE | Initial state — not yet connected. |
CONNECTING | Connection attempt in progress. |
CONNECTED | Socket connected, not yet authenticated. |
AUTHENTICATED | Socket connected and user authenticated. |
RECONNECTING | Lost connection, attempting reconnect. |
DISCONNECTED | Socket fully disconnected. |
ERROR | Connection error occurred. |
Methods
Connection
| Method | Description |
|---|---|
connect(): void | Manually initiate connection. Only needed if autoConnect: false. |
disconnect(): void | Disconnect socket and stop any pending reconnect timer. |
Authentication
| Method | Description |
|---|---|
authenticate(token: string): void | Send auth token to server. Must be called after connect. Throws if not connected. |
Direct Messaging (1:1)
| Method | Description |
|---|---|
sendMessage(to, content, metadata?) | Send a direct message to another user. |
markMessageRead(messageId, from) | Mark a received message as read. |
getMessageHistory(withUserId, opts?) | Fetch paginated DM history. opts: { limit?: number; before?: number } |
startTyping(to) | Send typing start indicator to a user. |
stopTyping(to) | Send typing stop indicator to a user. |
Presence
| Method | Description |
|---|---|
getOnlineStatus(userIds: string[]) | Request online status for a list of users. Listen for the online_status event for the response. |
Group Management
| Method | Description |
|---|---|
createGroup(name, members?, metadata?) | Create a new group. Listen for group_created. |
joinGroup(groupId) | Join an existing group. Listen for group_joined. |
leaveGroup(groupId) | Leave a group. Listen for group_left. |
getGroupMembers(groupId) | Fetch group members. Listen for group_members. |
getUserGroups() | Fetch all groups the current user belongs to. Listen for user_groups. |
Group Messaging
| Method | Description |
|---|---|
sendGroupMessage(groupId, content, metadata?) | Send a message to a group. |
markGroupMessageRead(groupId, messageId) | Mark a group message as read. |
getGroupMessageHistory(groupId, opts?) | Fetch paginated group history. opts: { limit?: number; before?: number } |
startGroupTyping(groupId) | Broadcast typing start to a group. |
stopGroupTyping(groupId) | Broadcast typing stop to a group. |
Voice Calling (P2P)
| Method | Description |
|---|---|
getIceServers() | Request ICE/TURN servers from the server. Listen for call_ice_servers. |
initiateCall(to, sdpOffer) | Start a P2P call. Listen for call_ringing then call_answered. |
answerCall(callId, sdpAnswer) | Accept an incoming call. Caller receives call_answered. |
rejectCall(callId) | Decline an incoming call. Caller receives call_rejected. |
hangUp(callId) | End an active or ringing call. Both parties receive call_ended. |
sendIceCandidate(callId, candidate) | Trickle an ICE candidate to the remote peer during call setup. |
Conference Calling (SFU)
| Method | Description |
|---|---|
joinConference(groupId) | Join (or create) a group conference. Listen for conference_token to connect to the SFU. |
leaveConference(groupId) | Leave a group conference. If last participant, the SFU room is torn down. |
Event Subscriptions
| Method | Description |
|---|---|
on(event, handler): () => void | Subscribe to an event. Returns an unsubscribe function. |
off(event, handler): void | Unsubscribe a specific handler. |
State
| Method | Returns | Description |
|---|---|---|
isConnected() | boolean | True if state is CONNECTED or AUTHENTICATED. |
isAuthenticated() | boolean | True if state is AUTHENTICATED. |
getConnectionState() | ConnectionState | Current state enum value. |
getUserId() | string | null | Authenticated userId, or null. |
onStateChange(listener) | () => void | Subscribe to state changes. Returns unsubscribe function. |
Events Reference
All events available via client.on(event, handler). In React, use useSocketEvent(event, handler).
Connection & Auth
| Event | Payload | Description |
|---|---|---|
connect | none | Socket connected to server. |
disconnect | string (reason) | Socket disconnected. Reconnection may follow. |
error | Error | Connection error. |
authenticated | { userId, socketId } | Authentication succeeded. |
auth_error | { message } | Authentication rejected by server. |
Presence
| Event | Payload | Description |
|---|---|---|
user_online | { userId } | A user came online. |
user_offline | { userId } | A user went offline (last socket disconnected). |
online_status | { statuses: Record<string, boolean> } | Bulk online status response. |
Direct Messages
| Event | Payload | Description |
|---|---|---|
message | { from, content, timestamp, messageId, metadata? } | Incoming direct message. |
message_delivered | { messageId } | Your message was delivered to recipient. |
message_read | { messageId, from? } | Recipient read your message. |
message_error | { messageId, error } | Message delivery failed. |
typing_start | { from } | Another user started typing to you. |
typing_stop | { from } | Another user stopped typing. |
message_history | { conversationId, messages: PersistedMessage[] } | Paginated DM history response. |
Groups & Group Messages
| Event | Payload | Description |
|---|---|---|
group_created | { group: Group } | New group created by you. |
group_create_error | { error } | Group creation failed. |
group_joined | { groupId, group, queuedMessages? } | Successfully joined a group. Includes offline messages. |
group_join_error | { groupId, error } | Failed to join group. |
group_left | { groupId } | You left a group. |
group_member_joined | { groupId, member: GroupMember } | A member joined a group you're in. |
group_member_left | { groupId, userId } | A member left a group you're in. |
group_members | { groupId, members: GroupMember[] } | Group members list response. |
user_groups | { groups: Group[] } | All groups the authenticated user belongs to. |
group_message | { groupId, messageId, from, content, timestamp, metadata? } | Incoming group message. |
group_message_delivered | { groupId, messageId } | Your group message was delivered. |
group_message_read | { groupId, messageId } | A group message was read. |
group_message_error | { groupId, messageId?, error } | Group message delivery failed. |
group_typing_start | { groupId, from } | A group member started typing. |
group_typing_stop | { groupId, from } | A group member stopped typing. |
group_message_history | { groupId, messages: PersistedMessage[] } | Paginated group history response. |
Voice Calls (P2P)
| Event | Payload | Description |
|---|---|---|
call_ice_servers | { iceServers: IceServer[] } | ICE/TURN server list response (includes STUN + generated TURN credentials). |
call_incoming | { callId, from, sdpOffer, iceServers } | Incoming call from another user. |
call_ringing | { callId } | Callee received your call initiation. |
call_answered | { callId, sdpAnswer } | Remote peer accepted the call with their SDP answer. |
call_rejected | { callId } | Remote peer declined the call. |
call_ended | { callId } | Call was ended by either party (or remote disconnect). |
call_busy | { callId } | Called user is already in another call. |
call_ice_candidate | { callId, candidate: IceCandidateInit } | Trickled ICE candidate from the remote peer. |
Conference Calls (SFU)
| Event | Payload | Description |
|---|---|---|
conference_token | { groupId, sfuUrl, token } | SFU access token — connect to sfuUrl using token. |
conference_started | { groupId, startedBy, participantCount } | First participant joined a group conference. |
conference_ended | { groupId } | Last participant left — conference is over. |
conference_participant_joined | { groupId, userId, participantCount } | A group member joined the active conference. |
conference_participant_left | { groupId, userId, participantCount } | A participant left the conference. |
conference_error | { groupId, error } | Conference join/leave error (e.g. not a group member). |
SocketProvider
React context provider. Wraps your component tree and makes all hooks available.
import { SocketProvider } from 'sockr-client';
function App() {
return (
<SocketProvider
config={{ url: "http://localhost:3000", transports: ['websocket'] }}
token={authToken}
>
{children}
</SocketProvider>
);
}
When token is provided, the provider calls client.authenticate(token) automatically once the socket connects. If token changes, it will re-authenticate.
useSocket()
Access the raw SocketClient instance and current connection state.
const {
client, // SocketClient | null
isConnected, // boolean
isAuthenticated, // boolean
connectionState, // ConnectionState enum
userId, // string | null
} = useSocket();
useSocketEvent(event, handler)
Subscribe to any socket event. Automatically unsubscribes on component unmount. Handler is called with the event payload.
useSocketEvent('message', (data) => {
console.log('New message from', data.from, ':', data.content);
});
useSocketEvent('user_online', ({ userId }) => {
setOnlineUsers(prev => new Set(prev).add(userId));
});
useMessages()
Collects all incoming direct messages into a local array. Listens for the message event automatically.
const {
messages, // Array<{ id, from, content, timestamp, metadata? }>
addMessage, // (message) => void — manually add a message
clearMessages // () => void — clear all messages
} = useMessages();
useSendMessage()
Send direct messages with loading and error state. isSending resets when message_delivered or message_error is received.
const {
sendMessage, // (to: string, content: string, metadata?) => void
isSending, // boolean
error, // string | null
} = useSendMessage();
// Usage
sendMessage('user-456', 'Hello!');
sendMessage('user-456', 'With metadata', { priority: 'high' });
usePresence()
Track online/offline status. Maintains a Set of online user IDs, updated from user_online, user_offline, and online_status events.
const {
onlineUsers, // Set<string> — set of online userIds
isUserOnline, // (userId: string) => boolean
checkOnlineStatus // (userIds: string[]) => void — triggers online_status event
} = usePresence();
// Check if a user is online
if (isUserOnline('user-123')) {
console.log('User is online');
}
// Bulk check
checkOnlineStatus(['user-1', 'user-2', 'user-3']);
useTypingIndicator(typingTimeout?)
For direct message conversations. Tracks who is typing. Typing indicators auto-clear after typingTimeout ms (default: 3000) if no stop event is received.
const {
startTyping, // (to: string) => void
stopTyping, // (to: string) => void
usersTyping, // Set<string> — userIds currently typing
} = useTypingIndicator(3000);
// In a chat input
<input
onFocus={() => startTyping(recipientId)}
onBlur={() => stopTyping(recipientId)}
onChange={() => startTyping(recipientId)}
/>
// Display
{[...usersTyping].map(id => (
<span key={id}>{id} is typing...</span>
))}
useGroup(groupId)
Manages membership state for a single group. Keeps the members list in sync by listening to group_member_joined and group_member_left events. Automatically fetches members once joined.
const {
group, // Group | null — full group object
members, // GroupMember[] — current member list
isJoined, // boolean
join, // () => void — emit group_join
leave, // () => void — emit group_leave
refreshMembers, // () => void — re-fetch members from server
} = useGroup('group-id-123');
// Usage
if (!isJoined) join();
console.log(group?.name, 'has', members.length, 'members');
useGroupMessages(groupId, options?)
Manages messages for a single group. Auto-fetches history on mount. Handles queued messages from the group_joined payload. Deduplicates by message ID on reconnect replay.
const {
messages, // GroupMessage[] — sorted by timestamp
isLoadingHistory, // boolean
sendMessage, // (content, metadata?) => void
markRead, // (messageId) => void
fetchHistory, // (before?: number) => void — load older messages
clearMessages, // () => void
} = useGroupMessages('group-id-123', {
fetchHistory: true, // auto-fetch on mount (default: true)
limit: 50, // messages per page (default: 50)
});
// Paginate older messages
fetchHistory(messages[0]?.timestamp); // pass oldest timestamp as 'before'
useGroupTyping(groupId, stopDelay?)
Tracks typing indicators for a group. Excludes the local user from typingMembers. startTyping() debounces — calling it repeatedly on each keypress auto-stops after stopDelay ms of inactivity. Stale indicators auto-clear after stopDelay × 2 ms.
const {
typingMembers, // string[] — userIds currently typing (excludes you)
startTyping, // () => void — call on keypress
stopTyping, // () => void — call on blur or message send
} = useGroupTyping('group-id-123', 2000);
// In a group chat input
<input
onKeyDown={startTyping}
onBlur={stopTyping}
placeholder="Type a message..."
/>
{typingMembers.length > 0 && (
<p>{typingMembers.join(', ')} {typingMembers.length === 1 ? 'is' : 'are'} typing...</p>
)}
useUserGroups()
Returns the authenticated user's group list. Fetches on mount. Auto-updates when groups are created, joined, or left (listens to group_created, group_joined, group_left events).
const {
groups, // Group[] — all groups the user belongs to
isLoading, // boolean
refresh, // () => void — re-fetch from server
createGroup, // (name, members?, metadata?) => void
} = useUserGroups();
// Create a group
createGroup('Engineering', ['user-2', 'user-3'], { private: true });
// Display groups
groups.map(g => (
<div key={g.id}>
{g.name} — {g.members.length} members
</div>
))
useVoiceCall(options?)
Full WebRTC P2P call lifecycle. Manages RTCPeerConnection, ICE candidate buffering, and local/remote media streams. Supports audio-only or audio+video calls.
const {
callState, // CallState — see values below
incomingCall, // IncomingCall | null — set when another user calls you
localStream, // MediaStream | null
remoteStream, // MediaStream | null
currentCallId, // string | null
startCall, // (userId: string) => Promise<void>
answerCall, // () => Promise<void> — reads from incomingCall automatically
rejectCall, // () => void
hangUp, // () => void
} = useVoiceCall({ video: false }); // video: false is the default
// Initiate a call
await startCall('user-456');
// Incoming call — incomingCall is set automatically by the hook
if (incomingCall) {
// Show UI: incomingCall.from, then:
await answerCall(); // accepts the call
// or: rejectCall();
}
Options
| Option | Type | Default | Description |
|---|---|---|---|
video | boolean | false | Request camera access and include video track in the offer. |
CallState
type CallState = 'idle' | 'calling' | 'ringing' | 'active' | 'busy' | 'ended'
| Value | Description |
|---|---|
'idle' | No active or pending call. |
'calling' | Outgoing call initiated, waiting for ringing confirmation. |
'ringing' | Caller: callee's device is ringing. Callee: incoming call received. |
'active' | Call is connected, media is flowing. |
'busy' | Callee was already in a call. |
'ended' | Call just ended (transitions back to 'idle' on cleanup). |
useConferenceCall(sfuAdapter)
SFU-based group conference calling. Handles join/leave, SFU connection lifecycle, and participant tracking. Pass an ISFUClientAdapter instance — use the included LiveKitClientAdapter or implement your own.
import { LiveKitClientAdapter } from 'sockr-client';
// npm install livekit-client (peer dependency)
const adapter = new LiveKitClientAdapter();
const {
conferenceState, // ConferenceState — see values below
participants, // ConferenceCallParticipant[] — { userId: string }[]
activeGroupId, // string | null
error, // string | null
joinConference, // (groupId: string) => void
leaveConference, // () => void
onRemoteTrack, // (handler: (track, participantId) => void) => void
} = useConferenceCall({ adapter, audio: true, video: false });
// Join a group conference
joinConference('group-id-123');
// Handle incoming remote media tracks
onRemoteTrack((track, participantId) => {
const el = document.getElementById(`video-${participantId}`) as HTMLVideoElement;
el.srcObject = new MediaStream([track]);
});
ConferenceState
type ConferenceState = 'idle' | 'joining' | 'active' | 'ended' | 'error'
| Value | Description |
|---|---|
'idle' | Not in any conference. |
'joining' | Join requested, waiting for SFU token from server. |
'active' | Connected to the SFU room, media can flow. |
'ended' | Conference ended (by the server or last participant leaving). |
'error' | Conference join or SFU connection failed. Check error. |
LiveKitClientAdapter
Install the LiveKit client SDK as a peer dependency:
npm install livekit-client
The adapter lazily requires livekit-client at runtime, so it won't affect bundle size if conference calling is unused.
ISFUClientAdapter interface
Implement this to use a different SFU (mediasoup, Janus, etc.):
interface ISFUClientAdapter {
connect(sfuUrl: string, token: string, opts?: SFUConnectOptions): Promise<void>;
disconnect(): Promise<void>;
publishTracks(opts?: SFUPublishOptions): Promise<void>;
onTrackSubscribed(handler: (track: MediaStreamTrack, participantId: string) => void): void;
onTrackUnsubscribed(handler: (track: MediaStreamTrack, participantId: string) => void): void;
onParticipantConnected(handler: (participantId: string) => void): void;
onParticipantDisconnected(handler: (participantId: string) => void): void;
onDisconnected(handler: () => void): void;
}
React Native
Sockr client works with React Native. Use WebSocket-only transport — polling is not supported in React Native environments.
import { SocketProvider } from 'sockr-client';
function App() {
return (
<SocketProvider
config={{ url: "https://your-server.com", transports: ['websocket'] }}
token={authToken}
>
<YourApp />
</SocketProvider>
);
}
// Or with vanilla SocketClient
import { SocketClient } from 'sockr-client';
const client = new SocketClient({
url: 'https://your-server.com',
transports: ['websocket'],
});