Skip to content

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
    }),
    ];
  2. Set proper Seller name in Vendure Dashboard > Settings > Sellers. The plugin uses Seller entity to identify your store, mainly it’s name field - this is how your store customers will see your name in chat widget when communicate. So set the proper name of Seller, e.g. My Awesome Store.

In a case you have Multi-vendor Marketplace, this plugin also can serve well. No extra configuration of plugin is needed. As stated above, the plugin uses Seller entity to identify your store, so each vendor will have its own chat space with customers.

  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>
)
}

In a case you have Multi-vendor Marketplace, you do not need to specify the CHAT_WIDGET_STORE_ID and CHAT_WIDGET_STORE_NAME env variables, as each vendor will have its own chat space with customers based on Seller entity.

Instead of that, you have to fetch Seller info from the product/variant data and pass it to the ChatWidget component, so storeId and storeName should look like this:

// pseudo code
const storeId = product.seller.id;
const storeName = product.seller.name;

The way how to get the seller name for each variant/product depends on your Multi-vendor Marketplace implementation. There is a Discord discussion about it which may help to figure it out what way works better for your use case.

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