Skip to content
kitn AI/UI

Svelte

kai-* web components work in Svelte without wrappers or special directives. The Svelte compiler sets DOM properties directly when you pass non-primitive values, and on:eventname wires up CustomEvents — no boilerplate required.

Terminal window
npm install @kitn.ai/ui

Register every kai-* element once, near your app entry point:

// src/main.js or inside a <script> block in your root component
import '@kitn.ai/ui/elements';

One import covers your entire app. No CSS import is needed — each element is styled inside its own Shadow DOM. Add @kitn.ai/ui/theme.css only if you want to override design tokens (see Theming).

The rule for every kai-* element: rich data in as properties, interactions out as events.

Svelte maps cleanly to this pattern:

WhatSvelte syntaxNotes
String attributeprop="value"Standard HTML attribute
Array or object{prop} shorthandSvelte assigns as a DOM property — never stringified
Listen for an eventon:kai-eventname={handler}handler(e) — data is on e.detail

<kai-chat> is transport-agnostic: you own the API call, the element owns the UI. Pass a messages array and handle kai-submit to stream replies back into state.

<script>
import '@kitn.ai/ui/elements';
let messages = [
{ id: '1', role: 'assistant', content: 'Hello! How can I help?' },
];
async function handleSubmit(e) {
const userText = e.detail.value;
const history = [
...messages,
{ id: crypto.randomUUID(), role: 'user', content: userText },
];
messages = history;
const assistantId = crypto.randomUUID();
messages = [...history, { id: assistantId, role: 'assistant', content: '' }];
let reply = '';
for await (const token of streamFromYourAPI(history)) {
reply += token;
// Reassign the array so Svelte detects the update.
messages = messages.map((m) =>
m.id === assistantId ? { ...m, content: reply } : m
);
}
}
</script>
<div style="display: flex; flex-direction: column; height: 100dvh;">
<kai-chat
{messages}
suggestions={['Summarize the chat', 'Start fresh']}
on:kai-submit={handleSubmit}
style="flex: 1; min-height: 0;"
/>
</div>

kai-chat is display: block and fills its container. Wrap it in a flex column and give it flex: 1 rather than a hard-coded height so it adapts to the viewport.

Compose <kai-conversations> alongside <kai-chat> for a multi-thread layout. Each element takes its data as properties and emits its own events.

<script>
import '@kitn.ai/ui/elements';
let conversations = loadConversations();
let activeId = conversations[0]?.id;
let messages = loadMessages(activeId);
function selectConversation(e) {
activeId = e.detail.id;
messages = loadMessages(activeId);
}
</script>
<div style="display: flex; height: 100dvh;">
<kai-conversations
{conversations}
{activeId}
on:kai-conversation-select={selectConversation}
on:kai-new-chat={() => startNewConversation()}
style="width: 300px; flex-shrink: 0;"
/>
<kai-chat
{messages}
on:kai-submit={(e) => sendMessage(e.detail.value)}
style="flex: 1; min-width: 0;"
/>
</div>
ElementEvente.detail
kai-chatkai-submit{ value: string }
kai-chatkai-value-change{ value: string }
kai-chatkai-model-change{ model: string }
kai-conversationskai-conversation-select{ id: string }
kai-conversationskai-new-chat{}
kai-conversationskai-toggle-sidebar{}
kai-attachmentskai-remove{ id: string }
kai-resizablekai-change{ sizes: number[] }

Every element’s full prop and event list is on its component page in the Components reference.