@wmtkrishan/chat-widget
v1.2.3
Published
Embeddable chat widget for React and Next.js applications
Downloads
343
Maintainers
Readme
@wmtkrishan/chat-widget
Embeddable live chat widget for React and Next.js. Connects to ECVue guest APIs for ticket creation, real-time messaging via Socket.IO, and session resume.
Features
- Floating widget (closed by default) with open, minimize, and close states
- Welcome flow: name, email, phone, then first message (General category by default)
- Guest registration via user-service (
sessionId+ticketIdpersisted insessionStorage) - Real-time chat via Socket.IO (send/receive, typing indicators, read receipts)
- REST fallback for message send when socket is unavailable
- Session resume on page reload (history + reconnect)
- Live chat header with wait timer until agent joins
- Session feedback UI when conversation is resolved/closed
- Settings panel (read-only contact details, transcript download, maximize, notifications)
- Self-contained CSS with Shadow DOM isolation — no style conflicts with host Tailwind, Bootstrap, or global resets
- Compatible with React (Vite) and Next.js App Router
Installation
npm install @wmtkrishan/chat-widgetConfiguration
Configure REST and WebSocket endpoints via configureChatWidget() or props.
| Setting | Description |
|---------|-------------|
| apiBaseUrl | KrakenD gateway for REST (e.g. http://localhost:8000) |
| socketUrl | WebSocket edge for Socket.IO (e.g. ws://localhost:8090) — not the KrakenD port |
| platformCode | Platform header value (default: ecvue) |
| supportHotline | Hotline number shown when a message fails to send (default placeholder: XXXXXX) |
React (Vite)
Create src/config/chatWidget.ts:
export const chatWidgetConfig = {
apiBaseUrl: import.meta.env.VITE_API_BASE_URL ?? '',
socketUrl: import.meta.env.VITE_SOCKET_URL ?? '',
platformCode: 'ecvue',
supportHotline: import.meta.env.VITE_SUPPORT_HOTLINE ?? '',
};Add .env:
VITE_API_BASE_URL=http://localhost:8000
VITE_SOCKET_URL=ws://localhost:8090
VITE_SUPPORT_HOTLINE=0221XXXXXXXInitialize in main.tsx:
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { configureChatWidget } from '@wmtkrishan/chat-widget';
import { chatWidgetConfig } from './config/chatWidget';
import App from './App';
configureChatWidget(chatWidgetConfig);
createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
</StrictMode>,
);Next.js (App Router)
Create config/chatWidget.ts:
export const chatWidgetConfig = {
apiBaseUrl: process.env.NEXT_PUBLIC_API_BASE_URL ?? '',
socketUrl: process.env.NEXT_PUBLIC_SOCKET_URL ?? '',
platformCode: 'ecvue',
};Add .env.local:
NEXT_PUBLIC_API_BASE_URL=http://localhost:8000
NEXT_PUBLIC_SOCKET_URL=ws://localhost:8090Use in a client component:
'use client';
import { ChatWidget, configureChatWidget } from '@wmtkrishan/chat-widget';
import { chatWidgetConfig } from '../config/chatWidget';
configureChatWidget(chatWidgetConfig);
export default function ChatWidgetDemo() {
return <ChatWidget />;
}Styles: By default the widget mounts in a Shadow DOM with bundled CSS, so you do not need to import
dist/style.cssand host-page styles will not leak in. For legacy setups, passisolateStyles={false}and import the CSS file (see Style isolation).
Usage
import { ChatWidget } from '@wmtkrishan/chat-widget';
function App() {
return <ChatWidget />;
}No separate CSS import is required — styles are injected into the widget's Shadow DOM automatically.
Or pass config as props:
<ChatWidget
apiBaseUrl="http://localhost:8000"
socketUrl="ws://localhost:8090"
platformCode="ecvue"
/>Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| agentName | string | "Watermelon" | Display name in the header after agent joins |
| agentStatus | string | "Available" | Status text below agent name |
| defaultOpen | boolean | false | Whether the widget starts open |
| className | string | "" | Additional class on the root container |
| apiBaseUrl | string | — | REST API base URL (overrides config) |
| socketUrl | string | — | Socket.IO WebSocket URL (overrides config) |
| platformCode | string | "ecvue" | Platform code sent to chat service |
| isolateStyles | boolean | true | Mount in Shadow DOM with bundled CSS (recommended). Set false to use global dist/style.css instead. |
Style isolation
The widget is designed to embed safely in any React or Next.js app without CSS conflicts:
| Mode | How | When to use |
|------|-----|-------------|
| Shadow DOM (default) | isolateStyles={true} — styles bundled inside the package | React, Next.js, Tailwind, MUI, Bootstrap hosts |
| Global CSS (legacy) | isolateStyles={false} + import '@wmtkrishan/chat-widget/dist/style.css' | Custom styling overrides, non-Shadow environments |
What is isolated:
- Host global resets, typography, and component libraries cannot restyle widget buttons, inputs, or bubbles.
- Widget CSS does not inject Tailwind preflight or other global rules into the host page.
- Image preview overlays render inside the same shadow root (not
document.body), so they stay styled correctly.
Next.js note: Use a client component ('use client') as shown above. No next.config CSS changes are required for the default Shadow DOM mode.
Guest chat flow
- User fills name, email, phone on welcome screen
POST /api/users/v1/public/guests→{ sessionId, ticketId, guestId, expiresAt }GET /api/chats/v1/public/guest/conversations/tickets/{ticketId}→ open/ensure conversationGET .../messages→ load history- Connect Socket.IO with
auth: { sessionId, platformCode } conversation:join { ticketId }→ live messaging viamessage:send/message:new- On page reload, valid session resumes from
sessionStorage
API integration
User service (public)
| Method | Path | When |
|--------|------|------|
| GET | /api/tickets/v1/public/categories | Background (General category id) |
| POST | /api/users/v1/public/guests | On first message send |
Create guest request body:
{
"name": "Jane Doe",
"email": "[email protected]",
"phone": "0771234567",
"categoryId": 1,
"subject": "Cannot log in"
}Chat service (guest, Bearer sessionId)
Base: /api/chats/v1/public/guest/conversations
| Method | Path | Purpose |
|--------|------|---------|
| GET | /tickets/{ticketId} | Open/ensure conversation |
| GET | /tickets/{ticketId}/messages?limit=50 | Message history |
| POST | /tickets/{ticketId}/messages | REST send fallback |
Headers: Authorization: Bearer <sessionId>, X-Platform-Code: ecvue
Ticket feedback (after resolved/closed)
Base: /api/tickets/v1/public/guest/ticket
Headers: Authorization: Bearer <guestSessionToken>
| Method | Path | Purpose |
|--------|------|---------|
| GET | /{ticketId}/feedback | Load existing rating (null if not rated) |
| POST | /{ticketId}/feedback | Submit/update { helpful, comment? } |
Shown in the widget as “Did we help you?” when the conversation ends.
Socket.IO
Connect to the WebSocket edge (e.g. ws://localhost:8090), not KrakenD.
import { io } from 'socket.io-client';
const socket = io('ws://localhost:8090', {
transports: ['websocket'],
auth: { sessionId: '<GUEST_SESSION_ID>', platformCode: 'ecvue' },
});Key events: conversation:join, message:send, message:new, typing, conversation:assigned, conversation:status
Advanced: useChat hook
'use client';
import { configureChatWidget, useChat } from '@wmtkrishan/chat-widget';
configureChatWidget({
apiBaseUrl: 'http://localhost:8000',
socketUrl: 'ws://localhost:8090',
});
function CustomChat() {
const { messages, sendMessage, guestSession, contactDetails } = useChat();
return (
<div>
{guestSession && <p>Ticket: {guestSession.ticketId}</p>}
{messages.map((m) => (
<p key={m.id}>{m.text}</p>
))}
<button onClick={() => sendMessage('Hello')}>Send</button>
</div>
);
}Exports
- Components:
ChatWidget - Hooks:
useChat - Config:
configureChatWidget,getChatWidgetConfig - Enums:
ConversationStatus,SenderType,MessageType - Types:
ChatWidgetProps,GuestSession,GuestConversation,Message, etc.
Example apps
| App | Path | Port | Package source |
|-----|------|------|----------------|
| Vite (local dev) | examples/vite-demo | 3700 | file:../.. |
| React | examples/react-demo | 3701 | npm |
| Next.js | examples/nextjs-demo | 3702 | npm |
# Library
npm install
npm run build
# Local dev demo (linked package)
cd examples/vite-demo
cp .env.example .env
npm install
npm run dev
# React demo (npm package)
cd examples/react-demo
cp .env.example .env
npm install
npm run dev
# Next.js demo (npm package)
cd examples/nextjs-demo
cp .env.example .env.local
npm install
npm run devDevelopment
npm install
npm run build # Build JS + CSS to dist/
npm run dev # Watch JS
npm run dev:css # Watch CSS
npm run typecheckPublishing
Log in to npm (scoped packages require 2FA):
npm login
npm run build
npm publish --access public --otp=YOUR_2FA_CODEAfter publishing, update example apps:
cd examples/react-demo && npm install @wmtkrishan/chat-widget@^0.4.0
cd examples/nextjs-demo && npm install @wmtkrishan/chat-widget@^0.4.0License
MIT
