@elqnt/chat
v3.1.0
Published
Platform-agnostic chat SDK for React and React Native
Readme
@elqnt/chat
Platform-agnostic chat SDK for React and React Native. Uses HTTP + SSE for reliable real-time communication.
Key Features:
- Mutations via HTTP -
startChat(),loadChat()return data directly (no waiting for SSE events) - Events via SSE - Real-time messages, typing indicators, agent updates
- Multi-transport - Browser EventSource, React Native fetch-based SSE, WhatsApp webhooks
- TypeScript-first - Full type definitions for all models and events
Installation
pnpm add @elqnt/chatTypeScript Configuration
This package uses subpath exports. Ensure your tsconfig.json includes a compatible moduleResolution setting:
{
"compilerOptions": {
"moduleResolution": "bundler"
}
}Supported values: "bundler", "node16", or "nodenext".
Expo / React Native
For Expo projects, add to your tsconfig.json:
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"moduleResolution": "bundler"
}
}Quick Start
import { useChat } from "@elqnt/chat/hooks";
function ChatComponent() {
const {
connect,
disconnect,
sendMessage,
startChat,
loadChat,
messages,
isConnected,
currentChat,
} = useChat({
baseUrl: "https://api.example.com/chat",
orgId: "org-123",
userId: "user-456",
});
useEffect(() => {
connect();
return () => disconnect();
}, []);
// Start a new chat - returns chatKey immediately
const handleNewChat = async () => {
const chatKey = await startChat({ agentId: "support-agent" });
console.log("Created chat:", chatKey);
};
// Load existing chat - returns Chat immediately
const handleLoadChat = async (chatKey: string) => {
const chat = await loadChat(chatKey);
console.log("Loaded chat with", chat.messages.length, "messages");
};
const handleSend = async (text: string) => {
await sendMessage(text);
};
return (
<div>
<ConnectionStatus connected={isConnected} />
<MessageList messages={messages} />
<MessageInput onSend={handleSend} />
</div>
);
}Architecture
┌─────────────────────────────────────────────────────────────┐
│ Your App │
├─────────────────────────────────────────────────────────────┤
│ useChat Hook │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ State │ │ Events │ │ Chat Operations │ │
│ │ messages │ │ onMessage │ │ sendMessage │ │
│ │ chatKey │ │ on(type) │ │ startChat │ │
│ │ isConnected│ │ │ │ loadChat │ │
│ └─────────────┘ └─────────────┘ └─────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Transport Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ SSE │ │ SSE-Fetch │ │ WhatsApp │ │
│ │ (Browser) │ │ (React │ │ (Backend │ │
│ │ │ │ Native) │ │ Webhook) │ │
│ └──────────────┘ └──────────────┘ └──────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Chat Server │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ POST /create, /send, /load, /end, /typing, /event │ │
│ │ GET /stream (SSE) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘Exports
/hooks - React Hooks
import { useChat } from "@elqnt/chat/hooks";useChat Options:
| Option | Type | Required | Description |
|--------|------|----------|-------------|
| baseUrl | string | Yes | Chat server URL |
| orgId | string | Yes | Organization ID |
| userId | string | Yes | User ID |
| clientType | "customer" \| "humanAgent" \| "observer" | No | Client type (default: "customer") |
| transport | ChatTransport \| "sse" \| "sse-fetch" | No | Transport to use |
| onMessage | (event: ChatEvent) => void | No | Event callback |
| onError | (error: TransportError) => void | No | Error callback |
| autoConnect | boolean | No | Connect on mount |
| debug | boolean | No | Enable logging |
useChat Return:
interface UseChatReturn {
// Connection
connect: () => Promise<void>;
disconnect: () => void;
connectionState: TransportState;
isConnected: boolean;
// Chat Operations (return data directly via HTTP)
startChat: (metadata?: Record<string, unknown>) => Promise<string>; // Returns chatKey
loadChat: (chatKey: string) => Promise<Chat>; // Returns loaded Chat
sendMessage: (content: string, attachments?: unknown[]) => Promise<void>;
sendEvent: (event: Omit<ChatEvent, "timestamp">) => Promise<void>;
endChat: (reason?: string) => Promise<void>;
// Typing Indicators
startTyping: () => void;
stopTyping: () => void;
// State (auto-updated by hook)
currentChat: Chat | null;
chatKey: string | null;
messages: ChatMessage[];
error: TransportError | null;
metrics: ConnectionMetrics;
// Events (SSE push events)
on: (eventType: string, handler: (event: ChatEvent) => void) => () => void;
clearError: () => void;
}Note:
startChat()andloadChat()use direct HTTP calls and return data immediately. You don't need to listen fornew_chat_createdorload_chat_responseevents - those are only broadcast for multi-tab synchronization.
/api - Low-Level API Functions
import {
// Browser API (uses @elqnt/api-client)
getChatHistoryApi,
getChatApi,
updateChatApi,
deleteChatApi,
getActiveChatsApi,
getActiveChatsCountApi,
// Stream API (direct HTTP)
createChat,
sendChatMessage,
loadChat,
endChat,
sendTypingIndicator,
sendEvent,
} from "@elqnt/chat/api";/models - TypeScript Types
import type {
Chat,
ChatMessage,
ChatEvent,
ChatEventTypeTS,
ChatUser,
ChatStatusTS,
MessageStatusTS,
Attachment,
} from "@elqnt/chat/models";/transport - Transport Adapters
import {
createSSETransport, // Browser EventSource
createFetchSSETransport, // React Native / Fetch-based
createWhatsAppTransport, // WhatsApp Business API (stub)
} from "@elqnt/chat/transport";
import type {
ChatTransport,
TransportConfig,
TransportState,
TransportError,
ConnectionMetrics,
CreateChatOptions,
CreateChatResponse,
LoadChatOptions,
LoadChatResponse,
} from "@elqnt/chat/transport";Transport Options
Browser (Default)
Uses native EventSource for SSE. This is the default and recommended transport for browser environments.
const chat = useChat({
baseUrl: "...",
orgId: "...",
userId: "...",
// transport defaults to "sse"
});React Native
Uses fetch with ReadableStream for SSE. Required for React Native since it doesn't have native EventSource.
import { createFetchSSETransport } from "@elqnt/chat/transport";
const transport = createFetchSSETransport();
const chat = useChat({
baseUrl: "...",
orgId: "...",
userId: "...",
transport,
});Custom Transport
Implement the ChatTransport interface for custom integrations:
import type { ChatTransport } from "@elqnt/chat/transport";
const customTransport: ChatTransport = {
// Connection
async connect(config) { /* ... */ },
disconnect() { /* ... */ },
// Mutations (return data directly)
async createChat(options) { /* returns { chatKey } */ },
async loadChatData(options) { /* returns { chat, agentId? } */ },
// Fire-and-forget operations
async send(event) { /* ... */ },
async sendMessage(message) { /* ... */ },
// Event subscriptions
onMessage(handler) { /* returns unsubscribe */ },
on(eventType, handler) { /* returns unsubscribe */ },
// State
getState() { /* returns TransportState */ },
getMetrics() { /* returns ConnectionMetrics */ },
getError() { /* returns TransportError | undefined */ },
clearError() { /* ... */ },
};Event Types
Subscribe to specific events:
const { on } = useChat({ ... });
// Subscribe to typing events
const unsubscribe = on("typing", (event) => {
console.log("User typing:", event.userId);
});
// Clean up
unsubscribe();Common event types:
| Event | Description |
|-------|-------------|
| message | New message received |
| typing | User started typing |
| stopped_typing | User stopped typing |
| new_chat_created | New chat session created |
| load_chat_response | Chat loaded |
| chat_ended | Chat session ended |
| human_agent_joined | Human agent joined chat |
| human_agent_left | Human agent left chat |
| agent_execution_started | AI agent started processing |
| agent_execution_ended | AI agent finished processing |
WhatsApp Integration
WhatsApp Business API integration happens via backend webhooks. The createWhatsAppTransport is a stub that documents the integration pattern.
Backend Setup
- Create a Meta Business App with WhatsApp integration
- Configure webhook URL pointing to your chat service
- Implement webhook handlers:
// Webhook verification
func HandleWhatsAppWebhook(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
verifyToken := r.URL.Query().Get("hub.verify_token")
if verifyToken == os.Getenv("WHATSAPP_VERIFY_TOKEN") {
w.Write([]byte(r.URL.Query().Get("hub.challenge")))
return
}
http.Error(w, "Invalid verify token", 403)
return
}
// Handle incoming message
var payload WhatsAppPayload
json.NewDecoder(r.Body).Decode(&payload)
// Map to chat event and publish
chatEvent := mapWhatsAppToChatEvent(payload)
publishToChatService(chatEvent)
}Helper Functions
import { mapWhatsAppToChatEvent } from "@elqnt/chat/transport";
// In your webhook handler
const events = mapWhatsAppToChatEvent(webhookPayload, orgId);
events.forEach(event => publishToChatService(event));Migration from v2
Breaking Changes in v3.0
- UI Components Removed - Build your own UI
- Contexts Removed - Use
useChathook directly - WebSocket Removed - SSE only (more reliable)
- Redux Integration Removed - Use hook state
Changes in v3.0.1
loadChat()returnsPromise<Chat>- Previously returnedPromise<void>, now returns the loaded chat directlystartChat()returns immediately - No longer waits for SSE event, returns chatKey from HTTP response- Transport interface extended - Added
createChat()andloadChatData()methods for direct HTTP responses - Event handlers simplified -
new_chat_createdandload_chat_responseevents are now only for multi-tab sync
Migration Steps
// Before (v2)
import { WebSocketChatCustomerProvider, useWebSocketChatCustomer } from "@elqnt/chat";
function App() {
return (
<WebSocketChatCustomerProvider config={...}>
<ChatComponent />
</WebSocketChatCustomerProvider>
);
}
function ChatComponent() {
const { messages, sendMessage } = useWebSocketChatCustomer();
return <ChatUI messages={messages} onSend={sendMessage} />;
}
// After (v3)
import { useChat } from "@elqnt/chat/hooks";
function ChatComponent() {
const { messages, sendMessage, connect, disconnect } = useChat({
baseUrl: "...",
orgId: "...",
userId: "...",
});
useEffect(() => {
connect();
return () => disconnect();
}, []);
return <ChatUI messages={messages} onSend={sendMessage} />;
}Removed Exports
WebSocketChatCustomerProviderWebSocketChatAdminProvideruseWebSocketChatCustomeruseWebSocketChatAdminuseWebSocketChatBase- All UI components (
ChatMessages,ChatInput, etc.)
Development
# Build
pnpm build
# Type check
pnpm typecheck
# Watch mode
pnpm devLicense
MIT
