# Svelte

Use kai-* web components in Svelte with property binding for arrays and objects, and on:kai-event listeners for CustomEvents.

`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.

## Install

```bash
npm install @kitn.ai/ui
```

Register every `kai-*` element once, near your app entry point. Registration is async, and Svelte assigns object properties as it mounts, so mount behind `elementsReady`. A property written before the element upgrades is discarded.

```js
// src/main.js

// Resolves once every kai-* element is defined.
elementsReady.then(() => mount(App, { target: document.getElementById('app') }));
```

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](/guides/theming/)).

## Pass data and handle events

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

Svelte maps cleanly to this pattern:

| What | Svelte syntax | Notes |
|---|---|---|
| String attribute | `prop="value"` | Standard HTML attribute |
| Array or object | `{prop}` shorthand | Svelte assigns as a DOM property — never stringified |
| Listen for an event | `on:kai-eventname={handler}` | `handler(e)` — data is on `e.detail` |

> **tip:** 
Svelte detects non-primitive values and assigns them as DOM properties rather than HTML attributes. This means `{messages}` always delivers a live JS array to the element — you never need `bind:this` just to set a property.

## Example — streaming chat

`<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.

A message is an **ordered `parts` array**, not a string: text, reasoning, tool calls, generative-UI cards and file attachments, in the order the model produced them. Fold each token onto the existing parts with the kit's own `appendTextPart` rather than replacing `parts` with a single text part, which throws away every other kind.

```svelte
<script lang="ts">

  let messages: ChatMessage[] = [
    { id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello! How can I help?' }] },
  ];

  async function handleSubmit(e: CustomEvent<{ value: string }>) {
    const userText = e.detail.value;
    const history: ChatMessage[] = [
      ...messages,
      { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: userText }] },
    ];
    messages = history;

    const assistantId = crypto.randomUUID();
    messages = [...history, { id: assistantId, role: 'assistant', parts: [] }];

    for await (const token of streamFromYourAPI(history)) {
      // Reassign the array so Svelte detects the update.
      messages = messages.map((m) =>
        m.id === assistantId ? { ...m, parts: appendTextPart(m.parts, token) } : 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.

## Add a conversation sidebar

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

```svelte
<script>

  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>
```

## Key events reference

| Element | Event | `e.detail` |
|---|---|---|
| `kai-chat` | `kai-submit` | `{ value: string }` |
| `kai-chat` | `kai-value-change` | `{ value: string }` |
| `kai-chat` | `kai-model-change` | `{ model: string }` |
| `kai-conversations` | `kai-conversation-select` | `{ id: string }` |
| `kai-conversations` | `kai-new-chat` | `{}` |
| `kai-conversations` | `kai-toggle-sidebar` | `{}` |
| `kai-attachments` | `kai-remove` | `{ id: string }` |
| `kai-resizable` | `kai-change` | `{ sizes: number[] }` |

Every element's full prop and event list is on its component page in the [Components](/components/attachments/) reference.
