The fastest way to a working support widget: describe it in JSON, not code. This guide takes you from nothing to a self-registering script tag.
Two doors to the same file
Section titled “Two doors to the same file”Run the wizard and answer a few questions:
npm create kaiIt asks for a project name first, then what you’re building. Pick “Embedded widget” and it writes <name>.construct.json in a new directory. Every answer maps onto one JSON key, nothing asked twice and nothing invented. Skip the wizard and hand-write the same file if you’d rather; both paths land on the same construct.
Either way, preview it live:
npx @kitn.ai/ui dev widget.construct.jsonThat works with no install. Once @kitn.ai/ui is installed as a dependency, the bare kai dev widget.construct.json command works too. Either way it starts a server with reload-on-edit: change the JSON, see the widget update.
The construct, annotated
Section titled “The construct, annotated”Here’s a construct using the full widget surface, more than the guided wizard walks you through. The wizard asks for shape, header title, home (on/off, plus a greeting title), starters, attachments, history, and accent color; everything else here (theme.unreadColor, home.recentConversation, home.links) you add by hand. The $schema line gives your editor autocomplete for exactly that.
{ "$schema": "https://ui.kitn.ai/schemas/construct/v1.json", "name": "acme-support", "layout": "widget", "provider": { "mode": "mock" }, "header": { "title": "Acme Support" }, "theme": { "unreadColor": "#38BDF8" }, "home": { "greeting": { "title": "How can we help?" }, "recentConversation": true, "links": [ { "label": "Docs", "href": "https://ui.kitn.ai" }, { "label": "Contact us" } ] }, "capabilities": { "starters": ["Track my order", "Talk to a human"], "history": { "persistence": "local" }, "conversations": true }}$schemapoints editors at the construct schema for autocomplete and inline validation, so a typo likelayloutgets flagged before you run anything.layout: "widget"makes this a floating launcher over an existing page. Use"fullscreen"when the chat is the whole page.provider: { mode: "mock" }is the keyless path: streamed replies come from a built-in mock model, so you get a working preview with no API key and no backend. Switch to a real endpoint by changing this one object, for example{ "mode": "endpoint", "url": "/api/chat" }. See the schema for the full provider shape.theme.unreadColortints the launcher’s unread badge; every other theme token stays at the kit’s default until you set it.hometurns on a Home/Messages tab bar.recentConversation: trueneedscapabilities.conversationsto have anything to show;kai validatewarns if you set one without the other. A link without anhref(like “Contact us” here) is still valid; wire it up to open your own dialog ormailto:link later.capabilities.historypersists the thread inlocalStorage; setting it is whyconversations: trueis required alongside it, so there’s a list to persist into.
Save it, then check it directly:
npx @kitn.ai/ui validate widget.construct.jsonCompile it
Section titled “Compile it”Once the preview looks right, compile to a single self-registering file:
npx @kitn.ai/ui compile widget.construct.jsonThis emits dist-construct/<name>.js: one file, no externals, that registers a <kai-chat>-backed widget when you load it. Drop it into any page with a <script type="module" src="...">; the generated source sits alongside it in dist-construct/source/ if you want to eject and customize further.
What’s next
Section titled “What’s next”- Getting Started: wire a
<kai-chat>by hand, register the elements, stream a reply. - Theming: every token beyond
unreadColor.