Skip to main content
The VIZOCHOK widget is a lightweight JavaScript SDK (@vizochok/widget) that embeds an AI-powered shopping assistant into any web page. It uses Shadow DOM for style isolation and communicates with the VIZOCHOK backend over WebSocket for real-time streaming.

Prerequisites

  • A VIZOCHOK tenant with a public API key (pk_...)
  • Your store ID (configured in the admin panel)
Never use your secret key (sk_...) in frontend code. The widget requires a public key with chat scope only.

Installation

1

Install the package

npm install @vizochok/widget
2

Create a widget instance

import { VIZOCHOKWidget } from '@vizochok/widget';

const widget = new VIZOCHOKWidget({
  apiKey: 'pk_your_public_key',
  storeId: 'your-store-id',
});
3

Mount the widget

widget.mount();
This appends the widget to document.body and opens a WebSocket connection to the VIZOCHOK API. A floating chat button appears in the bottom-right corner.

Configuration

The VIZOCHOKWidget constructor accepts a configuration object with these properties:
PropertyTypeDefaultDescription
apiKeystringrequiredPublic API key (pk_...)
storeIdstringrequiredStore identifier
userIdstringundefinedAuthenticated customer ID for personalization and usage tracking
language'uk' | 'en' | 'ru''uk'UI language
apiBasestring'https://api.vizochok.com'API base URL
position'bottom-right' | 'bottom-left''bottom-right'Widget position on screen
botNamestring'Помічник'Bot display name shown in the chat header
botAvatarstringundefinedURL for the bot avatar image
placeholderstringundefinedCustom input placeholder text
welcomeMessagestringundefinedWelcome message shown when the chat opens

Theme Options

Customize the widget appearance via the theme property:
const widget = new VIZOCHOKWidget({
  apiKey: 'pk_your_public_key',
  storeId: 'your-store-id',
  theme: {
    primaryColor: '#2D8A4E',      // Brand color for buttons and accents
    fontFamily: "'Inter', system-ui, sans-serif",
    borderRadius: '12px',          // Corner radius
    mode: 'light',                 // 'light', 'dark', or 'auto'
    currency: 'UAH',               // Currency symbol for price display
  },
});

Event Callbacks

Register callbacks to react to widget events:
const widget = new VIZOCHOKWidget({
  apiKey: 'pk_your_public_key',
  storeId: 'your-store-id',

  // Called when the cart changes (item added, removed, updated, or cleared)
  onCartChanged: (cart) => {
    console.log('Cart updated:', cart.action);  // 'item_selected', 'item_removed', etc.
    console.log('Items:', cart.items);           // Full cart array
    console.log('Total:', cart.total);           // Cart total in currency
    updateYourCartUI(cart.items, cart.total);
  },

  // Called when a user clicks a product card
  onProductClick: (product) => {
    console.log('Product clicked:', product.sku, product.name);
    navigateToProductPage(product.sku);
  },

  // Called when the shopping session is completed
  onSessionComplete: (summary) => {
    console.log('Session done:', summary.item_count, 'items, total:', summary.total);
  },

  // Connection lifecycle
  onConnect: () => console.log('Widget connected'),
  onDisconnect: () => console.log('Widget disconnected'),

  // Error handling
  onError: (error) => {
    console.error('Widget error:', error.code, error.message);
  },
});

CartChangedEvent

The onCartChanged callback receives a CartChangedEvent object:
action
string
The type of change: item_selected, item_removed, quantity_updated, or cart_cleared.
sku
string
SKU of the affected product (empty for cart_cleared).
name
string
Name of the affected product.
quantity
number
Quantity of the affected product.
price
number
Price of the affected product.
items
CartItem[]
Full array of all items currently in the cart. Each item has sku, name, price, quantity, and optional promo_price and brand.
total
number
Current cart total.

Framework Examples

import { useEffect, useRef } from 'react';
import { VIZOCHOKWidget } from '@vizochok/widget';

function ShoppingAssistant({ userId }) {
  const widgetRef = useRef(null);

  useEffect(() => {
    const widget = new VIZOCHOKWidget({
      apiKey: 'pk_your_public_key',
      storeId: 'your-store-id',
      userId,
      language: 'uk',
      onCartChanged: (cart) => {
        // Dispatch to your state manager (Redux, Zustand, etc.)
        dispatch({ type: 'CART_UPDATED', payload: cart });
      },
    });

    widget.mount();
    widgetRef.current = widget;

    return () => widget.unmount();
  }, [userId]);

  return null; // Widget manages its own DOM via Shadow DOM
}

Inline Mode

By default, the widget renders as a floating button that opens a chat panel. You can embed it inline within a specific container instead:
const container = document.getElementById('chat-container');
widget.mount(container, { inline: true });
In inline mode:
  • The widget fills 100% width and height of the container
  • The floating toggle button is hidden
  • The chat panel is always open
  • You control visibility via the container’s CSS
<div id="chat-container" style="width: 400px; height: 600px;"></div>

<script>
  const widget = new VIZOCHOKWidget({
    apiKey: 'pk_your_public_key',
    storeId: 'your-store-id',
  });
  widget.mount(document.getElementById('chat-container'), { inline: true });
</script>

Switching Modes at Runtime

You can switch between inline and floating mode without losing the conversation:
// Switch to inline mode
widget.setMode('inline', document.getElementById('chat-panel'));

// Switch back to floating mode
widget.setMode('floating');

Programmatic Control

The widget instance exposes methods for programmatic control:
widget.open();      // Open the chat panel
widget.close();     // Close the chat panel
widget.toggle();    // Toggle open/closed
widget.newChat();   // Clear history and start a new conversation
widget.unmount();   // Remove widget from DOM, preserve session in sessionStorage
widget.destroy();   // Remove widget and clear all session data

Shadow DOM & Style Isolation

The widget renders inside a Shadow DOM, which means:
  • Your page styles do not affect the widget
  • The widget styles do not leak into your page
  • CSS custom properties are used for theming (set via the theme config)
Because the widget uses Shadow DOM, external CSS frameworks (Bootstrap, Tailwind) will not style the widget’s internals. Use the theme configuration object instead.

Content Security Policy (CSP)

If your site uses a strict Content Security Policy, add these directives:
connect-src https://api.vizochok.com wss://api.vizochok.com;
script-src https://cdn.vizochok.com;
style-src 'unsafe-inline';
The style-src 'unsafe-inline' is needed because the widget injects styles into its Shadow DOM. If your CSP does not allow inline styles, the widget’s UI will not render correctly.

Performance Notes

  • Bundle size: The SDK is ~15 KB gzipped with zero external dependencies.
  • Lazy connection: The WebSocket connection is established on mount(), not on instantiation.
  • Session persistence: Conversation state is saved to sessionStorage so the user can refresh the page without losing context.
  • Heartbeat: The widget maintains a heartbeat ping every 30 seconds. If no response is received within 60 seconds, the connection is automatically closed and reconnected.