Until now, the Kookee chat widget had a prerequisite nobody asked for: React.
That is fine if you already have React. It is a non-starter if your marketing site is WordPress, your store is Shopify, your docs are a static site generator, or your app is Vue or Angular. The widget answered questions from your help articles, and the only way to get it was to be running the right framework.
That prerequisite is gone. The chat widget now ships as a plain script tag.
One line
<script async src="https://kookee.dev/chat/latest.js" data-api-key="YOUR_API_KEY"></script>
Paste that before </body> and you have a floating assistant that answers from your published help center articles. No npm install, no build step, no bundler configuration, no framework.
Two things to get right before it works:
The real snippet, with your key already in it, is on your project's AI Chat page in the dashboard. Copy it from there rather than typing the URL by hand.
Add your site's domain to the project's Allowed Origins. Once the list has an entry, requests from anywhere else are rejected — which is what stops someone from lifting your key out of the page source and running your assistant on their own site.
A domain missing from that list is the most common reason the bubble appears but every question fails. If answers never arrive, check the origin list first.
What it actually answers
The widget is grounded in your published help articles. It searches them both semantically and by keyword, answers in natural language, and cites the articles it used so the reader can open the source.
This matters more than it sounds. An assistant that answers from your documentation is bounded by your documentation — it tells users what you actually wrote, and when it cannot answer, you have learned something specific about a gap rather than watching a general-purpose model invent a feature you do not ship.
Making it yours
Everything visual is a data attribute, so a non-developer can adjust the widget by editing one line of the page template:
<script
async
src="https://kookee.dev/chat/latest.js"
data-api-key="YOUR_API_KEY"
data-title="Support"
data-greeting="Hi! Ask me anything about the product."
data-placeholder="Type your question…"
data-launcher-label="Need help?"
data-position="bottom-left"
data-theme="auto"
data-primary-color="#ff6b00"
></script>
data-position takes bottom-right or bottom-left, data-theme takes light, dark or auto, and data-locale picks the language. data-base-url exists for pointing the widget at a different API host.
The widget renders inside a shadow root, which is the part that makes this safe to paste into a site you did not build. Your page's CSS cannot leak in and restyle the chat, and the chat's CSS cannot leak out and break your layout. A Shopify theme with aggressive global selectors will not touch it.
The JS API, for when attributes run out
Attributes cover the common case. When you want the assistant wired into your own interface, window.KookeeChat is there:
KookeeChat.open();
KookeeChat.close();
KookeeChat.toggle();
KookeeChat.sendMessage('How do I reset my password?');
KookeeChat.clearMessages();
So your existing "Contact support" link can open the assistant instead of a mail client:
document.querySelector('#support-link').addEventListener('click', (event) => {
event.preventDefault();
KookeeChat.open();
});
Events are subscriptions, and each returns its own unsubscribe function:
const unsubscribe = KookeeChat.on('sourceClick', (source) => {
analytics.track('help_article_opened', { slug: source.slug });
});
on() also takes open and close. And if you want full control over configuration rather than attributes, call init() yourself — it accepts everything the attributes do, plus a few things they cannot express:
KookeeChat.init({
apiKey: 'YOUR_API_KEY',
greeting: 'What can I help you with?',
suggestions: ['How do I get started?', 'What are the pricing plans?', 'How do I cancel?'],
hideLauncher: true,
zIndex: 9999,
offset: { x: 24, y: 96 },
onSourceClick: (source) => window.open(`/help/${source.slug}`, '_blank'),
onOpenChange: (isOpen) => console.log('chat open:', isOpen),
});
suggestions is worth setting. Starter questions are the difference between a user typing something and a user staring at an empty input deciding they will just email you instead. hideLauncher turns off the default bubble so you can trigger the widget from your own button, and destroy() tears the whole thing down again — useful in a single-page app when the user logs into an area where the assistant does not belong.
TypeScript users get typings at https://kookee.dev/chat/latest.d.ts.
Details that show up in real use
The conversation survives page navigation within the tab, so a user who asks a question on your pricing page and then clicks through to features does not lose the thread. Minimizing keeps the conversation too — only the New chat button clears it. There is a full-height expanded mode for longer answers, and the launcher label pill can be dismissed by users who would rather just see the bubble.
None of these are configurable knobs you have to think about. They are the reasons the widget does not feel like a bolted-on iframe.
If you are already on React
Nothing changes:
import { KookeeChatWidget } from '@kookee/react/chat';
That component is still the right choice in a React app — it lives inside your tree and takes props like any other component. The script tag is not a replacement for it. It is the answer to the much larger group of sites that were never going to install a React package to get a chat bubble, and now do not have to.