Licence
MIT
Version
1.0.18
Deps
1
Size
496 kB
Vulns
0
Weekly
2.4K
@paramms/chat-widget
Real-time embeddable chat widget for the Relay platform. Drop into any website with a single script tag — no build step required.
Install
npm install @paramms/chat-widgetQuick start — CDN (no npm, no build)
<div id="chat"></div>
<script type="module">
import { mount } from 'https://relay.paramms.com/index.js'
mount({
el: document.getElementById('chat'),
url: 'wss://api.paramms.com/ws',
profileId: 'YOUR_PROFILE_ID',
})
</script>React / Next.js
General support chat (ChatWidget)
'use client'
import { ChatWidget } from '@paramms/chat-widget/react'
export default function SupportPage({ session }) {
return (
<ChatWidget
url={process.env.NEXT_PUBLIC_RELAY_WS_URL}
profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}
userId={session?.user.id} // optional — anonymous if omitted
userName={session?.user.name} // optional — shown to agents
userEmail={session?.user.email} // optional — shown to agents
/>
)
}Marketplace / multi-listing chat (MarketplaceChat)
Two modes — one component:
On a listing page (with listingId) → opens that item's chat directly. No list. Buyer is already looking at the item.
'use client'
import { MarketplaceChat } from '@paramms/chat-widget/react'
// Listing detail page — floating bubble in bottom-right corner
export default function ListingPage({ car, session }) {
return (
<>
<YourPageContent />
<MarketplaceChat
url={process.env.NEXT_PUBLIC_RELAY_WS_URL}
profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}
listingId={car.id}
listingTitle={car.title} // shown in chat header
listingPrice={car.price} // shown as "$12,500" tag
listingMeta="214,500 km · LPG" // one line of detail
listingStatus="Available"
userId={session?.user.id} // optional — anonymous if omitted
userName={session?.user.name} // optional
launcher // floating bubble button
position="bottom-right"
/>
</>
)
}On a /messages page (without listingId) → shows the buyer's full thread list (WhatsApp-style). No conversation opens until they tap one.
// Dedicated inbox page — e.g. /messages
export default function MessagesPage({ session }) {
return (
<div style={{ height: '600px' }}>
<MarketplaceChat
url={process.env.NEXT_PUBLIC_RELAY_WS_URL}
profileId={process.env.NEXT_PUBLIC_RELAY_PROFILE_ID}
userId={session?.user.id}
userName={session?.user.name}
// no listingId = inbox mode, shows all threads
/>
</div>
)
}Old section below (kept for reference)
'use client'
import { ChatWidget } from '@paramms/chat-widget/react'
export default function SupportChat() {
return (
<ChatWidget
url="wss://api.paramms.com/ws"
profileId="YOUR_PROFILE_ID"
/>
)
}Identified users (no separate login needed)
Pass your own user's ID as the token and their details via user. Anonymous users work without any configuration.
<ChatWidget
url="wss://api.paramms.com/ws"
profileId="YOUR_PROFILE_ID"
token={currentUser.id} // your own stable user ID — ties history across devices
user={{
name: currentUser.name, // shown to agents instead of opaque ID
email: currentUser.email, // agents can follow up even after disconnect
avatar: currentUser.avatarUrl,
meta: {
plan: currentUser.plan,
accountId: currentUser.id,
},
}}
/>Cryptographically verified identity (Tier 4)
For marketplaces and financial services — the guest's identity is verified against your ECDSA key so it cannot be spoofed.
// 1. Generate a key pair (once, server-side)
// openssl ecparam -genkey -name prime256v1 -noout | openssl pkcs8 -topk8 -nocrypt -out private.pem
// openssl ec -in private.pem -pubout | base64 -w0 → paste in Dashboard → Domain → Guest identity linking
// 2. Sign a token per user (your backend)
import { createSign } from 'node:crypto'
const hdr = Buffer.from(JSON.stringify({ alg: 'ES256', typ: 'JWT' })).toString('base64url')
const pay = Buffer.from(JSON.stringify({ sub: userId, iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 3600 })).toString('base64url')
const sig = createSign('SHA256').update(`${hdr}.${pay}`).sign(privateKey, 'base64url')
const signedToken = `${hdr}.${pay}.${sig}`
// 3. Pass to widget — server verifies the signature automatically
<ChatWidget url="..." profileId="..." token={signedToken} />Marketplace / multi-item (one thread per listing)
<ChatWidget
url="wss://api.paramms.com/ws"
profileId="YOUR_PROFILE_ID"
subjectId={`car_${listing.id}`} // one conversation per item
showChatList={true} // guest can switch between their threads
/>Launcher (floating button)
<ChatWidget
url="wss://api.paramms.com/ws"
profileId="YOUR_PROFILE_ID"
launcher={true}
position="bottom-right"
accent="#4F63F5"
/>Internationalisation
<ChatWidget
url="wss://api.paramms.com/ws"
profileId="YOUR_PROFILE_ID"
i18n={{
placeholder: 'Écrivez un message…',
send: 'Envoyer',
offline: 'Nous sommes hors ligne pour l\'instant',
poweredBy: '', // empty string hides the footer
}}
/>RTL is detected automatically for Arabic, Hebrew, Persian and Urdu browsers.
All options
| Option | Type | Default | Description |
|---|---|---|---|
el |
HTMLElement |
required | Mount target |
url |
string |
required | WebSocket URL (wss://...) |
profileId |
string |
required | Domain profile ID |
token |
string |
auto-generated | Guest identity token — pass your user's stable ID to tie history across devices |
user |
UserInfo |
— | Name, email, avatar, custom metadata — shown to agents |
subjectId |
string |
— | Item ID for marketplace mode |
showChatList |
boolean |
false |
Show conversation switcher in header |
accent |
string |
#f5713c |
Brand colour (hex) |
launcher |
boolean |
false |
Render as floating button |
position |
'bottom-right' | 'bottom-left' |
'bottom-right' |
Launcher position |
translateLang |
string |
— | Auto-translate incoming messages (ISO language code) |
quickReplies |
string[] |
— | Pre-set reply chips shown above input |
i18n |
I18nStrings |
English | UI string overrides |
Development
npm install
npm run dev # Vite preview at http://localhost:5174/
npm run build # tsc → dist/
npm run build:bundle # Vite → standalone CDN bundle
npm test # 75 tests via vitest
npm run typecheck