@traiyani/chatsdk-vue
v1.2.1
Published
ChatSDK Vue.js SDK - Pre-built, plug-and-play real-time chat components for Vue 3
Maintainers
Readme
ChatSDK Vue.js SDK
Vue.js 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: install plugin, login, drop in
<ChatPanel /> - Fully isolated — all dependencies bundled, scoped CSS, no conflicts with your app
- Vue 3 Composition API
- Real-time messaging (WebSocket)
- Product-based conversations with metadata
- File / image / video uploads
- Block / unblock users
- Internationalization (English + Arabic with RTL)
- Dark mode (light / dark / auto)
- Typing indicators, read receipts, unread counts
- Built-in unread badge & incoming-message toast — works out of the box, no extra code needed
- CSS auto-injection — no manual stylesheet import required
Table of Contents
- Installation
- SDK Initialization (Plugin)
- User Authentication
- ChatPanel Component
- ChatPanel Props Reference
- Exposed Methods
- Theme Support
- Internationalization (i18n)
- TypeScript Support
- Troubleshooting
1. Installation
npm install @traiyani/chatsdk-vueThat's it. axios and socket.io-client are bundled inside the SDK — your website does not need to install them.
| Dependency | Minimum Version |
|---|---|
| vue | >= 3.0.0 |
Works with Vite, webpack, Laravel Mix, Nuxt 3, and any Vue 3 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-vue';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-vue/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
Register the SDK plugin once in your main.ts / main.js. This initializes the singleton SDK instance and makes it available to all components.
// main.ts
import { createApp } from 'vue';
import { ChatSDKPlugin } from '@traiyani/chatsdk-vue';
import App from './App.vue';
const app = createApp(App);
app.use(ChatSDKPlugin, {
apiBaseUrl: 'https://your-chat-api.com/api',
appId: 'your-app-id',
environment: 'production',
});
app.mount('#app');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 |
| enablePluginUnreadAndIncomingToast | boolean | No | true | Enables the built-in unread badge and incoming-message toast. Set to false only if you want to build your own notification UI (see Advanced section). |
Environment Variables
Config values can come from any source:
// Vite
app.use(ChatSDKPlugin, {
apiBaseUrl: import.meta.env.VITE_API_URL,
appId: import.meta.env.VITE_APP_ID,
environment: import.meta.env.VITE_ENVIRONMENT,
});
// Nuxt 3
const runtimeConfig = useRuntimeConfig();
app.use(ChatSDKPlugin, {
apiBaseUrl: runtimeConfig.public.chatApiUrl,
appId: runtimeConfig.public.chatAppId,
environment: runtimeConfig.public.chatEnvironment,
});3. User Authentication
Use the useChatSDK() composable 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-vue';
const { login } = useChatSDK();
// After your website login completes:
const chatUser = await login({
id: 'user-external-id', // Your app's user ID
name: 'Ahmed', // Display name
email: '[email protected]', // Email
});
console.log('Chat user logged in:', chatUser.name);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 Update Current User
Call updateCurrentUser() to update the logged-in user's profile. Only provided fields are updated; omitted fields remain unchanged.
const { updateCurrentUser } = useChatSDK();
// Update name only
await updateCurrentUser({ name: 'New Name' });
// Update multiple fields
await updateCurrentUser({
name: 'Ahmed Al-Rashidi',
email: '[email protected]',
avatarUrl: 'https://example.com/avatar.jpg',
});
// Pass null to clear optional fields
await updateCurrentUser({ email: null, avatarUrl: null });
// Merge metadata (server merges with existing metadata)
await updateCurrentUser({ metadata: { phone: '+966501234567' } });3.4 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.5 Reactive State
The composable exposes reactive state shared across all components:
const { currentUser, isInitialized, isConnected } = useChatSDK();
// In template:
// {{ 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.
<template>
<button @click="showChat = true">Chat with Seller</button>
<ChatPanel
v-model:visible="showChat"
mode="chat"
:target-user="{ id: product.ownerId }"
:product="{
productId: product.id,
productName: product.name,
productImage: product.image,
price: product.price,
currency: 'QAR',
category: product.category,
}"
:chat-metadata="{ source: 'product-page', adType: 'featured' }"
:external-group-id="myGroupId"
:locale="currentLocale"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ChatPanel } from '@traiyani/chatsdk-vue';
const showChat = ref(false);
const currentLocale = ref('en');
const myGroupId = 'optional-custom-group-id';
const product = {
id: 'CAR_123',
ownerId: 'seller-456',
name: 'Toyota Camry 2023',
image: 'https://example.com/car.jpg',
price: 25000,
category: 'Vehicles',
};
</script>4.2 Open Conversation List (mode = "conversations")
Opens a panel with the conversation list sidebar and chat window.
<template>
<button @click="showConversations = true">
My Messages
<span v-if="unreadCount > 0">{{ unreadCount }}</span>
</button>
<ChatPanel
ref="chatPanelRef"
v-model:visible="showConversations"
mode="conversations"
:locale="currentLocale"
:on-unread-count-change="(count) => unreadCount = count"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ChatPanel } from '@traiyani/chatsdk-vue';
const showConversations = ref(false);
const currentLocale = ref('en');
const unreadCount = ref(0);
const chatPanelRef = ref();
</script>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
v-model:visible="showChat"
mode="chat"
:target-user="{ name: 'Ahmed', email: '[email protected]' }"
:product="{ productId: '456', productName: 'Phone', productImage: 'https://...' }"
:chat-metadata="{ source: 'user-profile' }"
:locale="currentLocale"
/>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
v-model:visible="showChat"
mode="chat"
:target-user="{ id: 'seller-456' }"
:product="{ productId: 'CAR_123', productName: 'Car', productImage: '...' }"
/>
<!-- You provide your own groupId -->
<ChatPanel
v-model:visible="showChat"
mode="chat"
:target-user="{ id: 'seller-456' }"
:product="{ productId: 'CAR_123', productName: 'Car', productImage: '...' }"
:external-group-id="myCustomGroupId"
/>4.6 Complete Integration Example
Full working example — from install to running chat:
main.ts:
import { createApp } from 'vue';
import { ChatSDKPlugin } from '@traiyani/chatsdk-vue';
import App from './App.vue';
const app = createApp(App);
app.use(ChatSDKPlugin, {
apiBaseUrl: import.meta.env.VITE_CHAT_API_URL,
appId: import.meta.env.VITE_CHAT_APP_ID,
environment: 'production',
});
app.mount('#app');LoginPage.vue:
<script setup lang="ts">
import { useChatSDK } from '@traiyani/chatsdk-vue';
const { login } = useChatSDK();
async function handleWebsiteLogin(websiteUser) {
// ... your website login logic ...
// Then login to chat:
await login({
id: websiteUser.id,
name: websiteUser.name,
email: websiteUser.email,
});
}
</script>ProductPage.vue:
<template>
<div class="product-page">
<h1>{{ product.name }}</h1>
<p>{{ product.price }} QAR</p>
<button @click="showChat = true">Chat with Seller</button>
<ChatPanel
v-model:visible="showChat"
mode="chat"
:target-user="{ id: product.ownerId }"
:product="{
productId: product.id,
productName: product.name,
productImage: product.image,
price: product.price,
currency: 'QAR',
}"
:chat-metadata="{ source: 'product-page' }"
:locale="currentLocale"
:on-error="handleChatError"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ChatPanel } from '@traiyani/chatsdk-vue';
const showChat = ref(false);
const currentLocale = ref('en');
const product = {
id: 'CAR_123',
ownerId: 'seller-456',
name: 'Toyota Camry 2023',
image: 'https://example.com/car.jpg',
price: 25000,
};
function handleChatError(error: Error) {
console.error('Chat error:', error.message);
}
</script>Navbar.vue (conversation list + unread badge):
<template>
<nav>
<button @click="showMessages = true">
Messages
<span v-if="unread > 0" class="badge">{{ unread }}</span>
</button>
<ChatPanel
v-model:visible="showMessages"
mode="conversations"
:locale="currentLocale"
:on-unread-count-change="(count) => unread = count"
/>
<button @click="handleLogout">Logout</button>
</nav>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { ChatPanel, useChatSDK } from '@traiyani/chatsdk-vue';
const { logout } = useChatSDK();
const showMessages = ref(false);
const unread = ref(0);
const currentLocale = ref('en');
async function handleLogout() {
await logout();
// ... your website logout logic ...
}
</script>5. ChatPanel Props Reference
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| visible | boolean | Yes | — | Controls panel visibility. Use v-model:visible for two-way binding. |
| mode | 'chat' \| 'conversations' | Yes | '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). |
| onProductClick | (product: ProductClickInfo) => void | No | — | Callback when the user clicks the product bar in the chat window. Receives { productId, productName, productImage?, productMetadata? }. |
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. Exposed Methods
Access methods on the <ChatPanel /> ref:
<template>
<ChatPanel ref="chatPanelRef" ... />
</template>
<script setup>
import { ref } from 'vue';
const chatPanelRef = ref();
// Logout from chat (SDK + socket + clear tokens)
await chatPanelRef.value.logout();
// Get current unread conversations count
const count = await chatPanelRef.value.getUnreadCount();
</script>| 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 |
| openConversationById(conversationId) | Promise<void> | Opens the panel (emits update:visible) and loads that conversation. Also used internally when the built-in incoming-message toast is clicked. |
Unread Badge & Incoming-Message Toast
Once you install the plugin and call login(), everything below works automatically — no extra code needed.
What you get for free:
Unread badge — a red pill showing how many conversations have unread messages. Drop it anywhere in your template:
<button type="button">Messages <ChatSDKUnreadBadge /></button>Incoming-message toast — a dark bottom-right popup appears whenever someone sends you a new message. Clicking it opens that conversation.
Live count — the unread count updates in real time over the WebSocket. No polling needed.
Requirements:
- You must have a
<ChatPanel mode="conversations" v-model:visible="..." />mounted somewhere (e.g. your navbar). When the user clicks the toast, this panel opens the correct conversation. - The user must be logged in (
useChatSDK().login()) before counts appear.
Reading the count in script:
import { getChatSDKUnreadConversationsCount } from '@traiyani/chatsdk-vue';
const unread = getChatSDKUnreadConversationsCount();
// unread.value — number (reactive)Turning it off: If you want to handle unread counts and notifications yourself, disable the built-in behavior:
app.use(ChatSDKPlugin, {
// ...
enablePluginUnreadAndIncomingToast: false,
});Then see the Advanced section below.
Advanced: useUnreadConversationsCount
You can skip this section. The built-in badge and toast described above work without any of the code below. Only read on if you need one of these two things.
Scenario A — Run your own code when a message arrives
Use case: You want to send analytics, show a browser notification, or update something in your app whenever a new message comes in — but you still want the SDK's built-in toast to appear.
How to do it: Call the composable in a component that stays mounted (e.g. App.vue):
<script setup lang="ts">
import { useUnreadConversationsCount } from '@traiyani/chatsdk-vue';
import type { IncomingMessagePreview } from '@traiyani/chatsdk-vue';
useUnreadConversationsCount({
incomingMessageMode: 'callback',
onIncomingMessage: (preview: IncomingMessagePreview) => {
// This runs every time someone sends you a message.
// The built-in toast still appears — this is extra.
console.log('New message in', preview.conversationId);
console.log('Text:', preview.preview);
},
});
</script>Nothing else to configure. The SDK toast still works; your callback runs alongside it.
Scenario B — Build your own notification UI (replace the SDK toast)
Use case: You don't want the SDK's built-in toast. Instead you want to show your own notification banner, popup, or any custom UI when a message arrives.
Step 1. Disable the built-in toast in main.ts:
app.use(ChatSDKPlugin, {
// ...your config...
enablePluginUnreadAndIncomingToast: false,
});Step 2. Use the composable to get reactive data you can bind to your template:
<script setup lang="ts">
import { useUnreadConversationsCount } from '@traiyani/chatsdk-vue';
const {
unreadConversationsCount, // how many conversations have unread messages
hasNewIncomingMessage, // true when a new message just arrived
lastIncomingMessage, // the message preview (sender, text, conversation id…)
clearNewIncomingMessage, // call this to dismiss your notification
} = useUnreadConversationsCount({
incomingMessageMode: 'refs',
realtime: true,
});
</script>
<template>
<!-- Your custom notification -->
<div v-if="hasNewIncomingMessage" class="my-notification">
{{ lastIncomingMessage?.senderName }}: {{ lastIncomingMessage?.preview }}
<button @click="clearNewIncomingMessage()">Dismiss</button>
</div>
<button>Messages ({{ unreadConversationsCount }})</button>
</template>Options & returns at a glance
| Option | What it does |
|---|---|
| incomingMessageMode: 'builtin' | Shows the SDK's built-in incoming-message toast. If the plugin is active this is already enabled automatically. |
| incomingMessageMode: 'callback' | Calls your onIncomingMessage callback on each new message. Does not show the SDK toast on its own — but if the plugin is active, the toast still appears alongside your callback. |
| incomingMessageMode: 'refs' | Fills hasNewIncomingMessage / lastIncomingMessage so you can build custom UI. No toast. |
| incomingMessageMode: 'none' | No incoming-message handling from this call. |
| realtime: true (default) | Keeps the unread count in sync over WebSocket. |
| realtime: false | Disables live updates for this instance. |
| Return value | Type | Description |
|---|---|---|
| unreadConversationsCount | Ref<number> | Conversations with unread messages |
| hasNewIncomingMessage | Ref<boolean> | true when a new preview is available |
| lastIncomingMessage | Ref<IncomingMessagePreview \| null> | Sender name, text snippet, conversation id |
| clearNewIncomingMessage() | () => void | Resets hasNewIncomingMessage to false |
| refresh() | () => Promise<void> | Re-fetches the unread count from the server |
Note:
notifyOnIncomingMessage: trueis deprecated. UseincomingMessageMode: 'refs'instead.The user must be logged in (
useChatSDK().login()) before counts appear.
7. Theme Support
<script setup>
import { useTheme } from '@traiyani/chatsdk-vue';
const { theme, actualTheme, isDark, setTheme, toggleTheme } = useTheme();
</script>
<template>
<div>
<p>Current: {{ actualTheme }}</p>
<button @click="toggleTheme">Toggle</button>
<button @click="setTheme('dark')">Dark</button>
<button @click="setTheme('light')">Light</button>
<button @click="setTheme('auto')">Auto (system)</button>
</div>
</template>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
v-model:visible="showChat"
mode="conversations"
:locale="currentLocale"
/>If you need direct access to i18n functions:
import { useI18n } from '@traiyani/chatsdk-vue';
const { t, changeLanguage, getCurrentLanguage, isRTL } = useI18n();
const title = t('conversations_title');
changeLanguage('ar');9. TypeScript Support
Full type declarations are included. No extra @types/* packages needed.
import type {
ChatSDKConfig,
ChatSDKUser,
ChatSDKMessage,
ChatSDKConversation,
ChatSDKLoginUser,
ProductContext,
TargetUser,
Product,
ChatState,
IncomingMessagePreview,
} from '@traiyani/chatsdk-vue';
// inject(unreadKey) — ChatSDKUnreadConversationsCountKey is exported as a value (InjectionKey)ChatSDKConfig includes optional enablePluginUnreadAndIncomingToast. Exported components include ChatPanel, ChatSDKUnreadBadge, and IncomingMessageToast.
10. Troubleshooting
| Problem | Solution |
|---------|----------|
| "SDK not initialized" | Make sure app.use(ChatSDKPlugin, config) is called in main.ts before app.mount() |
| "No logged-in user" | Call useChatSDK().login() after your website login, before opening ChatPanel |
| WebSocket not connecting | Check that apiBaseUrl is correct and reachable. Verify CORS allows your domain. |
| CSS not applying | CSS is auto-injected on import. If styles are missing, delete node_modules and reinstall. |
| Authentication fails | Double-check apiBaseUrl and appId match your chat server. |
| Panel not showing | Make sure v-model:visible is bound to a ref set to true. |
| Toast click does nothing | You need a <ChatPanel mode="conversations" v-model:visible="..." /> mounted somewhere. The toast opens conversations through that panel. |
| Toast shows wrong chat | Update to the latest SDK version. This was fixed in a recent release. |
| Unread badge shows 0 / toast never appears | Confirm login() completed successfully and enablePluginUnreadAndIncomingToast is not set to false. Check browser console for socket errors. |
License
MIT
