npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@connectycube/vendure-plugin-chat-widget

v0.14.0

Published

Vendure plugin for buyer-seller communication via chat widget

Readme

Vendure chat widget plugin

Vendure plugin to integrate Chat Widget for seller/buyer communication

Features

  • Easy Integration
    • Simple script to copy-paste on your website, no coding required
    • No need to handle backend infrastructure — ConnectyCube takes care of it
  • Superior feature set
    • Not just another basic chat widget - it's a complete chat system!
  • Customizable UI
    • Modify colors, themes, and layout to match your brand’s design
  • Real-time Messaging
    • Smooth, instant communication with no delays
  • Moderation tools
    • Keep chats safe with message filtering, user bans, and admin controls
  • Multimedia support
    • Send images, files, and emojis for richer conversations

Installation

Backend

  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:

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

  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.

Multi-vendor Marketplaces

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.

Storefront

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

Multi-vendor Marketplace

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.

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:

Have an issue?

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

Community

License

Apache 2.0