Skip to content

Chat widget Medusa plugin

Medusa 2.0 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?

On storefront, once logged in and opened product page, there will be a Chat toggle button bottom right:

Screenshot 2025-05-07 at 16 35 22

Once clicked, a chat with seller will be opened where you can ask any product’s related questions:

Screenshot 2025-05-07 at 16 39 20

From Medusa 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-05-07 at 16 38 13

Installation

Backend

  1. Add plugin to your Medusa 2.0 core app:

    yarn add @connectycube/chat-widget-medusa-plugin
  2. Create ConnectyCube account https://connectycube.com/signup and application, obtain credentials

Screenshot 2025-05-07 at 15 19 59
  1. Add the following variables to your .env file:

    VITE_BACKEND_URL=http://localhost:9000
    VITE_CHAT_APP_ID=<YOUR CONNECTYCUBE APP ID>
    VITE_CHAT_AUTH_KEY=<YOUR CONNECTYCUBE AUTH KEY>
    • VITE_BACKEND_URL - The URL of your Medusa backend, required for custom admin components.
    • VITE_CHAT_APP_ID - This is essential for authenticating your application with the ConnectyCube platform and accessing its chat services.
    • VITE_CHAT_AUTH_KEY - This key is used to authorize your application and ensure secure communication with the ConnectyCube SDK.
  2. Add the following code to your medusa-config.ts file:

    module.exports = defineConfig({
    admin: {
    vite: (config) => {
    config.define["__VITE_CHAT_APP_ID__"] = JSON.stringify(process.env.VITE_CHAT_APP_ID);
    config.define["__VITE_CHAT_AUTH_KEY__"] = JSON.stringify(process.env.VITE_CHAT_AUTH_KEY);
    return {
    optimizeDeps: {
    include: ["qs", "eventemitter3", "@xmpp/iq/callee", "@xmpp/resolve", "@xmpp/session-establishment", "@xmpp/client-core", "@xmpp/sasl-plain", "@xmpp/stream-features", "@xmpp/resource-binding", "@xmpp/reconnect", "@xmpp/middleware", "@xmpp/sasl-anonymous", "@xmpp/websocket", "@xmpp/iq/caller", "@xmpp/sasl"], // Will be merged with config that we use to run and build the dashboard.
    },
    };
    },
    },
    projectConfig: { ... },
    plugins: [
    {
    resolve: "@connectycube/chat-widget-medusa-plugin",
    options: {},
    },
    ],
    })

    This code connect plugin and helps to resolve an issue similar to https://github.com/medusajs/medusa/issues/11248.

  3. Start the project:

    Terminal window
    yarn dev

Storefront

  1. Add chat widget to your Storefront app:

    yarn add @connectycube/chat-widget
  2. Add the following variables to your .env file:

    NEXT_PUBLIC_CHAT_APP_ID=<YOUR CONNECTYCUBE APP ID>
    NEXT_PUBLIC_CHAT_AUTH_KEY=<YOUR CONNECTYCUBE AUTH KEY>
    NEXT_PUBLIC_STORE_ID=<YOUR MEDUSA STORE ID>
    NEXT_PUBLIC_STORE_NAME=<YOUR MEDUSA STORE NAME>
  3. Create src/ChatWidget.tsx component with the following content:

    "use client"
    import React, { useEffect, useState } from "react"
    import ConnectyCubeChatWidget from "@connectycube/chat-widget/react19"
    import { StoreCustomer, StoreProduct } from "@medusajs/types"
    export interface ChatWidgetProps {
    customer: StoreCustomer | null
    product: StoreProduct
    chatPerProduct?: boolean
    }
    export default function ChatWidget({
    customer,
    product,
    chatPerProduct,
    }: 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 = process.env.NEXT_PUBLIC_STORE_ID
    const storeName = process.env.NEXT_PUBLIC_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={process.env.NEXT_PUBLIC_CHAT_APP_ID}
    authKey={process.env.NEXT_PUBLIC_CHAT_AUTH_KEY}
    userId={customer.id}
    userName={`${customer.first_name} ${customer.last_name}`}
    // 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>
    )
    }
  4. update tsconfig.json:

    {
    "compilerOptions": {
    "module": "nodenext",
    "moduleResolution": "nodenext",
    ...
    }
    }
  5. update storefront/src/app/[countryCode]/(main)/products/[handle]/page.tsx to retrieve customer info and pass it to ProductTemplate:

    const customer = await retrieveCustomer()
    return (
    <ProductTemplate
    product={pricedProduct}
    region={region}
    countryCode={params.countryCode}
    customer={customer}
    />
    )
  6. Finally, connect ChatWidget component on product details page, e.g. src/modules/products/templates/index.tsx

    <ChatWidget
    customer={customer}
    product={product}
    chatPerProduct={true}
    />

Demo

The complete demo app (backend + storefront) available https://github.com/ConnectyCube/chat-widget-medusa-plugin-demo-app

Have an issue?

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