Chat widget Vendure plugin
Overview
Section titled “Overview”Vendure commerce platform plugin to integrate Chat Widget for seller/buyer communication
With the plugin, store owners can:
- add live chat support to help customers in real-time
- enable buyer-seller messaging in multi-vendor marketplaces
- support group conversations or private threads
- record and persist messages securely via ConnectyCube’s backend
Use cases:
- Customer Support: Enable users to ask questions and get support without leaving the site
- Marketplace Messaging: Let buyers and vendors chat directly in a secure environment.
How can I use it?
Section titled “How can I use it?”On storefront, once logged in and opened product page, there will be a Chat toggle button bottom right where customers can contact the merchant:
From Vendure dashboard there will be a new page called Chat, with the widget embedded, where all customers’ chats are displayed, so you as a merchant can reply:
Installation
Section titled “Installation”Backend
Section titled “Backend”- Add plugin to your Vendure app:
yarn add @connectycube/vendure-plugin-chat-widget
- Create ConnectyCube account https://connectycube.com/signup and application, obtain credentials:
Also, go to Chat -> Custom Fields and create a new custom field called externalId
:
-
Add the following code to your
vendure-config.ts
file:import { ChatWidgetPlugin } from '@connectycube/vendure-plugin-chat-widget';plugins: [ChatWidgetPlugin.init({appId: ..., // ConnectyCube App IdauthKey: "", // ConnectyCube Auth KeystoreName: "", // A name of your store (any string, will be visible by buyer)storeId: "" // Some uniq identifier of your store (any uniq string)}),];
Storefront
Section titled “Storefront”- Add chat widget to your Storefront app. For example, let’s use Remix storefront starter as an example:
yarn add @connectycube/chat-widget yarn connectycube patch-ssr # Apply SSR patches for Remix to work well
- Add the following variables to your
.env
file:
CHAT_WIDGET_CONNECTYCUBE_APP_ID=<CONNECTYCUBE APP ID> CHAT_WIDGET_CONNECTYCUBE_AUTH_KEY="<CONNECTYCUBE AUTH KEY> CHAT_WIDGET_STORE_ID=<YOUR STORE ID> CHAT_WIDGET_STORE_NAME=<YOUR STORE NAME>
- Create
app/components/ChatWidget.tsx
component with the following content:
import { useEffect, useState } from 'react'; import ConnectyCubeChatWidget from '@connectycube/chat-widget';
type StoreCustomer = { id: string; firstName: string; lastName: string; };
type StoreProduct = { id: string; title: string; };
export type ChatWidgetEnv = { CHAT_WIDGET_STORE_ID: string; CHAT_WIDGET_STORE_NAME: string; CHAT_WIDGET_CONNECTYCUBE_APP_ID: string; CHAT_WIDGET_CONNECTYCUBE_AUTH_KEY: string; };
export interface ChatWidgetProps { customer: StoreCustomer | null; product: StoreProduct; chatPerProduct?: boolean; env: ChatWidgetEnv; }
export default function ChatWidget({ customer, product, chatPerProduct, env }: ChatWidgetProps) { const quickActions = { title: 'Quick Actions', description: 'Select an action from the options below or type a first message to start a conversation.', actions: [ "Hi, I'm interested in this product.", 'Can you tell me more about the price and payment options?', 'Is the product still available?', 'Can I schedule a viewing?', ], };
if (!customer) { return null; }
const [defaultChat, setDefaultChat] = useState<any>(null); const [isOpen, setIsOpen] = useState<boolean>(false);
const onOpenCloseWidget = (isOpen: boolean) => { setIsOpen(isOpen); };
const storeId = env.CHAT_WIDGET_STORE_ID; const storeName = env.CHAT_WIDGET_STORE_NAME;
useEffect(() => { if (isOpen) { console.log('Widget is open:', isOpen); const defaultChatKey = chatPerProduct ? product.id : storeId; const defaultChatName = chatPerProduct ? product.title : storeName;
setDefaultChat({ id: defaultChatKey, opponentUserId: storeId, type: 'group', name: defaultChatName, }); } }, [isOpen]);
return ( <div> <ConnectyCubeChatWidget // credentials appId={env.CHAT_WIDGET_CONNECTYCUBE_APP_ID} authKey={env.CHAT_WIDGET_CONNECTYCUBE_AUTH_KEY} userId={customer.id} userName={`${customer.firstName} ${customer.lastName}`} // settings showOnlineUsersTab={false} splitView={true} // quick actions quickActions={quickActions} // notifications showNotifications={true} playSound={true} // moderation enableContentReporting={true} enableBlockList={true} // last seen enableLastSeen={true} // url preview enableUrlPreview={true} limitUrlsPreviews={1} // attachments settings attachmentsAccept={'image/*,video/*,.pdf,audio/*'} // default chat defaultChat={defaultChat} onOpenChange={onOpenCloseWidget} /> </div> ); }
- update
remix.config.js
:
const commonConfig = { ... browserNodeBuiltinsPolyfill: { modules: { events: true } }, };
- Finally, connect
ChatWidget
component on product details page, e.g.app/routes/products.$slug.tsx
import ChatWidget, { ChatWidgetEnv } from '~/components/ChatWidget';
...
export async function loader({ params, request }: DataFunctionArgs) { ...
const { product } = await getProductBySlug(params.slug!, { request });
const activeCustomer = await getActiveCustomer({ request });
return json({ product: product!, activeCustomer, ENV: { CHAT_WIDGET_STORE_ID: process.env.CHAT_WIDGET_STORE_ID, CHAT_WIDGET_STORE_NAME: process.env.CHAT_WIDGET_STORE_NAME, CHAT_WIDGET_CONNECTYCUBE_APP_ID: process.env.CHAT_WIDGET_CONNECTYCUBE_APP_ID, CHAT_WIDGET_CONNECTYCUBE_AUTH_KEY: process.env.CHAT_WIDGET_CONNECTYCUBE_AUTH_KEY, }}) }
export default function ProductSlug() { ...
const { product, activeCustomer, ENV } = useLoaderData<typeof loader>();
return ( <div> ...
<ChatWidget customer={activeCustomer.activeCustomer!} product={{ title: product.name, id: product.id }} chatPerProduct={true} env={ENV as ChatWidgetEnv} /> </div> ) }
Have an issue?
Section titled “Have an issue?”Join our Discord for quick answers to your questions or file a GitHub issue