@traiyani/chatsdk-react
v1.0.14
Published
ChatSDK React SDK - Pre-built, plug-and-play real-time chat components for React
Maintainers
Readme
ChatSDK React SDK
React SDK for ChatSDK - Real-time chat solution with product-based conversations, Socket.IO, instant messaging, and file sharing. Fully isolated and self-contained.
Features
- Zero boilerplate — 3 steps: wrap with provider, login, use
<ChatPanel /> - Fully isolated — all dependencies bundled, no global side effects, scoped CSS
- React 16.8+ (hooks)
- Real-time messaging via Socket.IO
- Product-based conversations with metadata
- File/image/video uploads and sharing
- Block/unblock users
- Internationalization (English + Arabic with RTL)
- Dark mode support (light / dark / auto)
- Typing indicators, read receipts, unread counts
- CSS auto-injection — no manual stylesheet import required
Table of Contents
- Installation
- SDK Initialization (Provider)
- User Authentication
- ChatPanel Component
- ChatPanel Props Reference
- Ref Methods
- Theme Support
- Internationalization (i18n)
- TypeScript Support
- Troubleshooting
1. Installation
npm install @traiyani/chatsdk-reactThat's it. axios and socket.io-client are peer dependencies — install them if your project doesn't have them already:
npm install axios socket.io-client| Dependency | Minimum Version |
|---|---|
| react | >= 16.8.0 |
| react-dom | >= 16.8.0 |
| axios | >= 0.21.0 |
| socket.io-client | >= 4.0.0 |
Works with Vite, webpack, Next.js, Create React App, Parcel, and any React bundler out of the box.
CSS (Auto-Injected)
CSS is automatically injected when you import any SDK export. No manual stylesheet import is needed.
import { ChatPanel } from '@traiyani/chatsdk-react';All SDK styles are scoped under .chatsdk-root so they never conflict with your app's CSS (Bootstrap, Tailwind, etc.).
If you prefer to import the stylesheet manually (e.g., for SSR or custom loading order):
import '@traiyani/chatsdk-react/dist/chatsdk.css';Optional: Load Fonts
The SDK UI uses Mulish and Lato fonts. If your app doesn't already load them, add this to your HTML <head>:
<link href="https://fonts.googleapis.com/css2?family=Mulish:wght@400;500;600;700&family=Lato:wght@400;700&display=swap" rel="stylesheet">2. SDK Initialization
Wrap your app (or the chat-using subtree) with <ChatSDKProvider>. This initializes the singleton SDK instance and makes it available to all child components.
// index.tsx or App.tsx
import { ChatSDKProvider } from '@traiyani/chatsdk-react';
const sdkConfig = {
apiBaseUrl: 'https://your-chat-api.com/api',
appId: 'your-app-id',
environment: 'production' as const,
};
function Root() {
return (
<ChatSDKProvider config={sdkConfig}>
<App />
</ChatSDKProvider>
);
}Configuration Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| apiBaseUrl | string | Yes | — | Chat API server URL |
| appId | string | Yes | — | Your application ID |
| environment | 'development' \| 'staging' \| 'production' | Yes | — | Environment |
| enableLogging | boolean | No | false | Enable debug logging in console |
| autoConnect | boolean | No | true | Auto-connect WebSocket after auth |
| timeout | number | No | 30000 | HTTP request timeout in ms |
Environment Variables
Config values can come from any source:
// Create React App
const sdkConfig = {
apiBaseUrl: process.env.REACT_APP_API_URL,
appId: process.env.REACT_APP_APP_ID,
environment: process.env.REACT_APP_ENV,
};
// Vite
const sdkConfig = {
apiBaseUrl: import.meta.env.VITE_API_URL,
appId: import.meta.env.VITE_APP_ID,
environment: import.meta.env.VITE_ENVIRONMENT,
};
// Next.js
const sdkConfig = {
apiBaseUrl: process.env.NEXT_PUBLIC_API_URL,
appId: process.env.NEXT_PUBLIC_APP_ID,
environment: process.env.NEXT_PUBLIC_ENV,
};3. User Authentication
Use the useChatSDK() hook to login, verify users, and logout. These are the only auth calls your website needs to make.
3.1 Login the Current User
Call login() once after the website user authenticates (e.g., after website login). This saves the chat token and connects the WebSocket.
import { useChatSDK } from '@traiyani/chatsdk-react';
function AfterLogin() {
const { login } = useChatSDK();
const handleWebsiteLogin = async (websiteUser) => {
// ... your website login logic ...
// Then login to chat:
const chatUser = await login({
id: websiteUser.id, // Your app's user ID
name: websiteUser.name, // Display name
email: websiteUser.email, // Email
});
console.log('Chat user logged in:', chatUser.name);
};
return <button onClick={() => handleWebsiteLogin(myUser)}>Login</button>;
}What happens internally:
- Authenticates the user with the chat server (login or auto-register)
- Saves the auth token to localStorage
- Connects the WebSocket for real-time messaging
3.2 Verify Another User
Call verifyUser() to check if another user (e.g., a seller) exists in the chat system, or register them if they don't. This does NOT save a token or connect a socket — it's purely a lookup.
const { verifyUser } = useChatSDK();
const otherUser = await verifyUser({
id: 'seller-456',
name: 'Mohammed',
email: '[email protected]',
});
console.log('Verified:', otherUser.name, otherUser.id);3.3 Logout
Call logout() when the website user logs out. This disconnects the socket, clears the token, and cleans up all state.
const { logout } = useChatSDK();
await logout();3.4 Reactive State
The hook exposes shared state across all components:
const { currentUser, isInitialized, isConnected } = useChatSDK();
// In JSX:
// {currentUser?.name}
// {isConnected ? 'Online' : 'Offline'}4. ChatPanel Component
<ChatPanel /> is the main UI component. It handles everything internally: conversation layout, socket rooms, real-time events, language switching, and error containment.
4.1 Open a Direct Chat (mode = "chat")
Opens a chat popup with a specific user, optionally about a product.
import { useState } from 'react';
import { ChatPanel } from '@traiyani/chatsdk-react';
function ProductPage() {
const [showChat, setShowChat] = useState(false);
const product = {
id: 'CAR_123',
ownerId: 'seller-456',
name: 'Toyota Camry 2023',
image: 'https://example.com/car.jpg',
price: 25000,
category: 'Vehicles',
};
return (
<div>
<h1>{product.name}</h1>
<button onClick={() => setShowChat(true)}>Chat with Seller</button>
<ChatPanel
visible={showChat}
onVisibleChange={setShowChat}
mode="chat"
targetUser={{ id: product.ownerId }}
product={{
productId: product.id,
productName: product.name,
productImage: product.image,
price: product.price,
currency: 'QAR',
category: product.category,
}}
chatMetadata={{ source: 'product-page', adType: 'featured' }}
locale="en"
/>
</div>
);
}4.2 Open Conversation List (mode = "conversations")
Opens a panel with the conversation list sidebar and chat window.
import { useState, useRef } from 'react';
import { ChatPanel } from '@traiyani/chatsdk-react';
import type { ChatPanelRef } from '@traiyani/chatsdk-react';
function Navbar() {
const [showMessages, setShowMessages] = useState(false);
const [unread, setUnread] = useState(0);
const chatPanelRef = useRef<ChatPanelRef>(null);
return (
<nav>
<button onClick={() => setShowMessages(true)}>
Messages
{unread > 0 && <span className="badge">{unread}</span>}
</button>
<ChatPanel
ref={chatPanelRef}
visible={showMessages}
onVisibleChange={setShowMessages}
mode="conversations"
locale="en"
onUnreadCountChange={(count) => setUnread(count)}
/>
</nav>
);
}4.3 Chat with a User by Name + Email (No ID)
When you don't have the other user's external ID, pass their name and email. The SDK will authenticate/register them automatically.
<ChatPanel
visible={showChat}
onVisibleChange={setShowChat}
mode="chat"
targetUser={{ name: 'Ahmed', email: '[email protected]' }}
product={{ productId: '456', productName: 'Phone', productImage: 'https://...' }}
chatMetadata={{ source: 'user-profile' }}
locale="en"
/>4.4 Target User Resolution Logic
| What you pass | SDK behavior |
|---|---|
| { id: 'ext-123' } | Uses ID directly, skips user verification |
| { id: 'ext-123', name: 'Ahmed', email: '...' } | Uses ID directly, skips verification |
| { name: 'Ahmed', email: '[email protected]' } | Authenticates/registers user by name + email, then uses returned ID |
| { email: '[email protected]', phone: '+974...' } | Authenticates by email, phone stored as metadata |
Rule: If targetUser.id is present, the SDK skips verification. Otherwise, it authenticates the user first.
4.5 ExternalGroupId
The externalGroupId is a unique identifier for a conversation between two users (optionally about a product). It prevents duplicate conversations.
- If you provide
externalGroupId— the SDK uses it as-is. - If you omit it — the SDK generates one automatically using SHA-256 hash of the sorted user IDs + product ID.
{/* SDK generates groupId automatically */}
<ChatPanel
visible={showChat}
onVisibleChange={setShowChat}
mode="chat"
targetUser={{ id: 'seller-456' }}
product={{ productId: 'CAR_123', productName: 'Car', productImage: '...' }}
/>
{/* You provide your own groupId */}
<ChatPanel
visible={showChat}
onVisibleChange={setShowChat}
mode="chat"
targetUser={{ id: 'seller-456' }}
product={{ productId: 'CAR_123', productName: 'Car', productImage: '...' }}
externalGroupId={myCustomGroupId}
/>4.6 Complete Integration Example
Full working example — from install to running chat:
index.tsx:
import { ChatSDKProvider } from '@traiyani/chatsdk-react';
const sdkConfig = {
apiBaseUrl: process.env.REACT_APP_API_URL,
appId: process.env.REACT_APP_APP_ID,
environment: 'production' as const,
};
root.render(
<ChatSDKProvider config={sdkConfig}>
<App />
</ChatSDKProvider>
);LoginPage.tsx:
import { useChatSDK } from '@traiyani/chatsdk-react';
function LoginPage({ onLogin }) {
const { login } = useChatSDK();
const handleWebsiteLogin = async (websiteUser) => {
// ... your website login logic ...
await login({
id: websiteUser.id,
name: websiteUser.name,
email: websiteUser.email,
});
onLogin();
};
return <button onClick={() => handleWebsiteLogin(myUser)}>Login</button>;
}ProductPage.tsx:
import { useState } from 'react';
import { ChatPanel } from '@traiyani/chatsdk-react';
function ProductPage({ product }) {
const [showChat, setShowChat] = useState(false);
return (
<div>
<h1>{product.name}</h1>
<p>{product.price} QAR</p>
<button onClick={() => setShowChat(true)}>Chat with Seller</button>
<ChatPanel
visible={showChat}
onVisibleChange={setShowChat}
mode="chat"
targetUser={{ id: product.ownerId }}
product={{
productId: product.id,
productName: product.name,
productImage: product.image,
price: product.price,
currency: 'QAR',
}}
chatMetadata={{ source: 'product-page' }}
locale="en"
onError={(err) => console.error('Chat error:', err.message)}
/>
</div>
);
}Navbar.tsx (conversation list + unread badge):
import { useState, useRef } from 'react';
import { ChatPanel, useChatSDK } from '@traiyani/chatsdk-react';
import type { ChatPanelRef } from '@traiyani/chatsdk-react';
function Navbar() {
const { logout } = useChatSDK();
const [showMessages, setShowMessages] = useState(false);
const [unread, setUnread] = useState(0);
const handleLogout = async () => {
await logout();
// ... your website logout logic ...
};
return (
<nav>
<button onClick={() => setShowMessages(true)}>
Messages
{unread > 0 && <span className="badge">{unread}</span>}
</button>
<ChatPanel
visible={showMessages}
onVisibleChange={setShowMessages}
mode="conversations"
locale="en"
onUnreadCountChange={(count) => setUnread(count)}
/>
<button onClick={handleLogout}>Logout</button>
</nav>
);
}5. ChatPanel Props Reference
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| visible | boolean | Yes | — | Controls panel visibility. |
| onVisibleChange | (visible: boolean) => void | Yes | — | Called when panel wants to close. Use with visible for two-way binding. |
| mode | 'chat' \| 'conversations' | No | 'conversations' | 'chat' opens a direct chat popup. 'conversations' opens the conversation list with chat window. |
| targetUser | TargetUser | For mode="chat" | — | The other party to chat with. See Target User Resolution. |
| product | ProductContext | No | — | Product metadata to attach to the conversation. |
| chatMetadata | Record<string, any> | No | — | Additional metadata for the chat (e.g., source, ad type). |
| externalGroupId | string | No | — | Custom group ID. If omitted, the SDK generates one via SHA-256. |
| locale | 'en' \| 'ar' | No | — | Language. Reactive — changing it switches the language automatically. |
| onError | (error: Error) => void | No | — | Callback for errors. The panel catches all internal errors and calls this. |
| onUnreadCountChange | (count: number) => void | No | — | Callback when unread conversation count changes (polled every 30s). |
TargetUser
interface TargetUser {
id?: string; // External user ID — if present, skip verification
name?: string; // Display name
email?: string; // Email
phone?: string; // Phone number (stored as metadata)
}ProductContext
interface ProductContext {
productId: string;
productName: string;
productImage?: string;
price?: number;
currency?: string;
category?: string;
productMetadata?: Record<string, any>;
}6. Ref Methods
Access methods on the <ChatPanel /> via a React ref:
import { useRef } from 'react';
import { ChatPanel } from '@traiyani/chatsdk-react';
import type { ChatPanelRef } from '@traiyani/chatsdk-react';
function MyComponent() {
const chatPanelRef = useRef<ChatPanelRef>(null);
// Logout from chat (SDK + socket + clear tokens)
const handleLogout = async () => {
await chatPanelRef.current?.logout();
};
// Get current unread conversations count
const checkUnread = async () => {
const count = await chatPanelRef.current?.getUnreadCount();
console.log('Unread:', count);
};
return <ChatPanel ref={chatPanelRef} visible={showChat} onVisibleChange={setShowChat} />;
}| Method | Returns | Description |
|---|---|---|
| logout() | Promise<void> | Full cleanup: SDK logout + socket disconnect + clear tokens + close panel |
| getUnreadCount() | Promise<number> | Returns the number of conversations with unread messages |
7. Theme Support
import { useTheme } from '@traiyani/chatsdk-react';
function ThemeSwitch() {
const { theme, actualTheme, setTheme, toggleTheme } = useTheme();
return (
<div>
<p>Current: {actualTheme}</p>
<button onClick={toggleTheme}>Toggle</button>
<button onClick={() => setTheme('dark')}>Dark</button>
<button onClick={() => setTheme('light')}>Light</button>
<button onClick={() => setTheme('auto')}>Auto (system)</button>
</div>
);
}8. Internationalization (i18n)
Supported languages: English (en), Arabic (ar with RTL).
The <ChatPanel /> accepts a reactive locale prop and handles language switching internally. No manual calls needed.
<ChatPanel
visible={showChat}
onVisibleChange={setShowChat}
mode="conversations"
locale={currentLocale}
/>If you need direct access to i18n functions:
import { useI18n } from '@traiyani/chatsdk-react';
function MyComponent() {
const { t, changeLanguage, getCurrentLanguage, isRTL } = useI18n();
return (
<div>
<h1>{t('conversations_title')}</h1>
<button onClick={() => changeLanguage('ar')}>Switch to Arabic</button>
</div>
);
}9. TypeScript Support
Full type declarations are included. No extra @types/* packages needed.
import type {
ChatSDKConfig,
ChatSDKUser,
ChatSDKMessage,
ChatSDKConversation,
ChatSDKLoginUser,
ProductContext,
TargetUser,
Product,
ChatState,
ChatPanelProps,
ChatPanelRef,
} from '@traiyani/chatsdk-react';10. Troubleshooting
| Problem | Solution |
|---------|----------|
| "SDK not initialized" | Ensure <ChatSDKProvider> wraps your app in index.tsx before any SDK usage |
| "No logged-in user" | Call useChatSDK().login() after your website login before opening ChatPanel |
| WebSocket not connecting | Verify apiBaseUrl is correct and reachable, check CORS configuration |
| CSS not applying | CSS is auto-injected on import. If missing, clear node_modules and reinstall |
| Authentication fails | Verify apiBaseUrl and appId match your chat server configuration |
| Panel not showing | Ensure visible={true} is passed and onVisibleChange updates the state |
| Language not updating | Use useI18n() hook instead of direct t() import for reactive translations |
License
MIT
