Web Component + Wrappers@botmara/core · @botmara/react · @botmara/vue

Botmara Chat Widget

A tiny, framework-agnostic chat widget (<botmara-chat>) with React/Vue wrappers. Connect your API key and pass your own data as a knowledge base (KB). Customize the look (accent, radius, size), show a floating bubble or inline window, and wire optional notifications. Default upstream is https://api.botmara.com

We are working on allowing outside API key integrations on later versions.

Get an API Key

Install

Install exactly one wrapper for your stack. You don’t need to add @botmara/core directly when using the wrappers.

React
react.shbash
npm i @botmara/react
# or: pnpm add @botmara/react
yarn add @botmara/react
Vue 3
vue.shbash
npm i @botmara/vue
# or: pnpm add @botmara/vue
yarn add @botmara/vue
Plain HTML
index.htmlhtml
<script type="module" src="https://unpkg.com/@botmara/core/dist/botmara-chat.js"></script>
<botmara-chat bot-name="Helper"></botmara-chat>

Quick Start

Usage — ReactJS

Import the React wrapper and drop the component. If using Next.js, see notes below.

Bot.tsxtsx
import { BotmaraChat } from "@botmara/react";

export default function Bot(){
  return (
    <BotmaraChat
         mode="bubble"            // "bubble" | "inline"
      chipBg ="orange"
      position="bottom-right"  // or "bottom-left"
      color="#22c55e"
      radius={16}
      botName="Support"
      botDescription="Ask about pricing, shipping, or policies."
      sections={[
        { id:"pricing", title:"Pricing", content:"Free 1k/mo; Pro $19/mo" },
        { id:"shipping", title:"Shipping", content:"3–5 days; 30-day returns" },
      ]}
      kb={{ opening_hours: "8am-10pm", smoking-policy: "No smoking allowed" }}
    />
  );
}

Usage — Nextjs

Always use a client component and dynamic import with ssr:false in Next.js.

Bot.tsxtsx
"use client";
import dynamic from "next/dynamic";
const BotmaraChat = dynamic(() => import("@botmara/react").then(m => m.BotmaraChat), { ssr: false });

export default function Bot(){
  return (
    <BotmaraChat
        mode="bubble"            // "bubble" | "inline"
      position="bottom-right"  // or "bottom-left"
      color="#22c55e"
      radius={16}
      botName="Support"
      botDescription="Ask about pricing, shipping, or policies."
      sections={[
        { id:"pricing", title:"Pricing", content:"Free 1k/mo; Pro $19/mo" },
        { id:"shipping", title:"Shipping", content:"3–5 days; 30-day returns" },
      ]}
      kb={{ opening_hours: "8am-10pm", pet-policy: "No pets allowed" }}
    />
  );
}

Usage — Vue

Import the Vue wrapper and drop the component. Vite works out of the box.

App.vuevue
<script setup lang="ts">
import { BotmaraChat } from "@botmara/vue";
</script>

<template>
  <BotmaraChat
    mode="inline"
      botName="Support"
    :sections="[
      { id:'faq', title:'FAQ', content:'Ask me about returns, billing, or features.' }
    ]"
    :kb="{ faq: 'Returns 30 days. Billing via Stripe. Features: KB + sections.' }"
  />
</template>

Usage — HTML

index.htmlhtml
  <head>
    <meta charset="utf-8" />
    <title>BotMara – Demo</title>
<script type="module" src="https://cdn.jsdelivr.net/npm/@botmara/core/dist/botmara-chat.js" defer></script>
  </head>
  <body>
    <botmara-chat
      bot-name="Helper"
      color="#0ea5e9"
      sections='[{ "id":"faq", "title":"FAQ", "content":"Ask me about orders and refunds."}]'
      kb='{"faq":"Orders ship in 2–3 days. Refunds within 7 days."}'
    ></botmara-chat>
  </body>
</html>

Props & Attributes

React/Vue props map 1:1 to the web component attributes below. In plain HTML, boolean flags are presence-based (e.g. notify-sound).

Core
AttributeReact/Vue PropTypeDefaultNotes
modemode"bubble" | "inline"bubbleFloating bubble or inline panel
positionposition"bottom-right" | "bottom-left"bottom-rightBubble + window corner
sizesize"sm" | "md" | "lg" | "xl"mdPreset width/height
widthwidthnumber | stringOverrides --bm-width (px or CSS size)
heightheightnumber | stringOverrides --bm-height
radiusradiusnumber | string12pxSets --bm-radius (px or CSS size)
placeholderplaceholderstringType a message…Input placeholder
fallback-messagefallbackMessagestring“Sorry, I don’t have that yet. Please contact support.”Shown on errors/missing reply
bot-namebotNamestringAssistantHeader title
bot-descriptionbotDescriptionstringHow can I help you today?Header subtitle
Theme
AttributeReact/Vue PropTypeDefaultNotes
colorcolorstring#4f46e5Accent color (sets --botmara-accent)
bgbgstring#ffffffPanel background (--botmara-bg)
texttextstring#111827Main text (--botmara-text)
text-lighttextLightstring#6b7280Secondary text (--botmara-text-light)
user-bguserBgstringaccentUser bubble bg (--botmara-user-bg)
user-textuserTextstring#fffUser bubble text (--botmara-user-text)
assistant-bgassistantBgstring#f3f4f6Assistant bubble bg (--botmara-assistant-bg)
assistant-textassistantTextstringtextAssistant bubble text (--botmara-assistant-text)
chip-bgchipBgstringaccentChip background color (--botmara-chip-bg)
chip-light-textchipLightTextstring#fffChip light text color (--botmara-chip-light-text)
Data
AttributeReact/Vue PropTypeDefaultNotes
sectionssectionsSection[][]Clickable chips → content/prompt/autoSend
sections-modesectionsMode"merge" | "replace"mergeMerge/replace vs KB-derived chips
datadataRecord<string,string>{}Alias for kb/knowledge
knowledgeknowledgeRecord<string,string>{}Alias for kb
kbkbRecord<string,string>{}Small map used to seed/answer/refine
data-sectionsdataSectionsSection[] | stringified JSONAlias for sections (HTML attr convenience)
Net
AttributeReact/Vue PropTypeDefaultNotes
endpointendpointstringapiBase + /v1/chatFull URL or your /api proxy (⚒️ V0.0.3 coming soon)
api-baseapiBasestringhttps://api.botmara.comDefault upstream base; change anytime (V0.03 coming soon ⚒️ )
api-keyapiKeystringOnly for direct-to-upstream (avoid in public clients)
auth-typeauthType"x-api-key" | "bearer"x-api-keyHeader style when apiKey is present
Notify
AttributeReact/Vue PropTypeDefaultNotes
notify-soundnotifySoundbooleanfalsePing on assistant reply (needs user gesture)
notify-sound-opennotifySoundOpenbooleanfalseAllow sound even when window is focused
notify-browsernotifyBrowserbooleanfalseUse Notification API when granted
notify-toastnotifyToastbooleanfalseSoft toast near bubble
mutemutebooleanfalseDisable vibration/sound

Defaults (implementation): apiBase = https://api.botmara.com, path /v1/chat, size md, radius 12px, accent #4f46e5.

Theming & Size

Quick props cover most needs. You can also set CSS variables directly on the element.

custom.csscss
botmara-chat {
  --botmara-accent: #10b981; /* same as color prop */
  --botmara-bg: #fff;
  --botmara-text: #111827;
  --botmara-text-light: #6b7280;
  --botmara-user-bg: #10b981;
  --botmara-user-text: #fff;
  --botmara-assistant-bg: #f3f4f6;
  --botmara-assistant-text: #111827;
  --bm-radius: 14px;
  --bm-width: 420px;
  --bm-height: 640px;
}

Sections & KB

Sections are chips. If a section has content, it’s sent (and may be refined). If it has prompt, it pre-fills the input (optionally autoSend). If neither, the widget tries kb[section.id].

sections.tsxtsx
<BotmaraChat
  sections=[
    { id: "pricing", title: "Pricing", content: "Free 1k; Pro $19/mo; Scale" },
    { id: "shipping", title: "Shipping", prompt: "Do you ship to EU?" },
  ]
  kb={{ shipping: "Domestic 3–5 days; Intl 5–10 days." }}
  sectionsMode="merge"  // or "replace"
/>

Notifications

Opt-in via presence attributes. Audio needs a user gesture; browser notifications require permission.

notify.htmlhtml
<botmara-chat notify-sound notify-toast notify-browser></botmara-chat>

Events

The element emits send, response, error, sectionchange. In React, attach via a ref.

events.tsxtsx
"use client";
import { useEffect, useRef } from "react";
import dynamic from "next/dynamic";
const BotmaraChat = dynamic(() => import("@botmara/react").then(m => m.BotmaraChat), { ssr:false });

export default function WithEvents(){
  const ref = useRef<any>(null);
  useEffect(() => {
    const el = ref.current?.shadowRoot?.host ?? ref.current;
    if (!el) return;
    const onSend = (e: any) => console.log('send', e.detail);       // { text, section, sessionId }
    const onResp = (e: any) => console.log('response', e.detail);   // { answer, refined?, source?, ... }
    const onErr  = (e: any) => console.warn('error', e.detail);     // { code, message, type?, retryAfter?, headers? }
    const onSec  = (e: any) => console.log('sectionchange', e.detail); // { section }
    el.addEventListener('send', onSend);
    el.addEventListener('response', onResp);
    el.addEventListener('error', onErr);
    el.addEventListener('sectionchange', onSec);
    return () => {
      el.removeEventListener('send', onSend);
      el.removeEventListener('response', onResp);
      el.removeEventListener('error', onErr);
      el.removeEventListener('sectionchange', onSec);
    };
  }, []);
  return <BotmaraChat ref={ref} endpoint="/api/botmara/chat" />;
}

SSR & Framework Notes

  • Web components need the DOM; in Next.js use dynamic() with { ssr: false }.
  • Don’t import the core script on the server; only on the client (or via React/Vue wrappers).
  • Keep secrets off the client. The widget’s apiKey prop is for internal demos only.

Troubleshooting

"document is not defined" / "Objects are not valid as a React child ({ ssr })"
fix.txttext
Wrap the import with Next.js dynamic and ssr:false:

const BotmaraChat = dynamic(() => import("@botmara/react").then(m => m.BotmaraChat), { ssr:false });
Direct import in Node fails (ReferenceError: document)
fix.txttext
The core web component targets the browser. Use wrappers on the client side, or run it only after DOM is available.
No response / 401 / 429
fix.txttext
Check your proxy and API key. Surface a friendly fallbackMessage and back off on 429s.

FAQ

Do I need @botmara/core when using @botmara/react/@botmara/vue?

No — install only the wrapper for your stack. The wrapper brings the core under the hood.

Can I switch to a different upstream later?

Not yet. This is on our road map to allow you to Point endpoint at any compatible API or change apiBase.

How big can my KB be?

Keep it small on the free tier (a few hundred key-value pairs). Larger KBs require a paid plan as our servers would require more memory to process them.