Skip to content

Chat widget Vendure plugin

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.

On storefront, once logged in and opened product page, there will be a Chat toggle button bottom right where customers can contact the merchant:

Screenshot 2025-07-02 at 12 14 00

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:

Screenshot 2025-07-02 at 12 13 44
  1. Add plugin to your Vendure app:
yarn add @connectycube/vendure-plugin-chat-widget
  1. Create ConnectyCube account https://connectycube.com/signup and application, obtain credentials:
Screenshot 2025-06-04 at 10 36 59

Also, go to Chat -> Custom Fields and create a new custom field called externalId:

Screenshot 2025-07-02 at 12 24 35
  1. Add the following code to your vendure-config.ts file:

    import { ChatWidgetPlugin } from '@connectycube/vendure-plugin-chat-widget';
    plugins: [
    ChatWidgetPlugin.init({
    appId: ..., // ConnectyCube App Id
    authKey: "", // ConnectyCube Auth Key
    storeName: "", // A name of your store (any string, will be visible by buyer)
    storeId: "" // Some uniq identifier of your store (any uniq string)
    }),
    ];
  1. 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
  1. 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>
  1. 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>
);
}
  1. update remix.config.js:
const commonConfig = {
...
browserNodeBuiltinsPolyfill: { modules: { events: true } },
};
  1. 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>
)
}

Join our Discord for quick answers to your questions or file a GitHub issue