# Getting Started

Install @kitn.ai/ui, register the web components, and render a working chat UI in a few lines.

Drop `<kai-chat>` into a page, wire two things, and you have a live chat UI. This guide goes from install to an element that handles real submissions.

## Install

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

That's it for web components — SolidJS ships inside the bundle. (You only add `solid-js` separately if you import the native Solid components; see the [SolidJS guide](/guides/frameworks/solid/).)

## Register the elements

One import registers every `kai-*` element in the browser's custom element registry. Run it once — at the top of your entry file, or in a `<script type="module">`:

```js

```

> **tip:** 
Load the bundle straight from a CDN — no install, no build step:

```html
<script type="module">

</script>
```

Pin an exact version in production (e.g. `@kitn.ai/ui@0.20.1/dist/...`). The package is pre-1.0, so a minor release can break things.

## Your first chat

You'll lean on two patterns over and over: set rich data on a JavaScript property, and listen for the element's `CustomEvent`s. Here they are in plain HTML.

```html
<kai-chat id="chat" style="display:block; height:100dvh;"></kai-chat>

<script type="module">

  await customElements.whenDefined('kai-chat');

  const chat = document.getElementById('chat');

  // Seed the thread. Arrays can't be attributes, so this goes in JavaScript.
  chat.messages = [
    { id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello! How can I help?' }] },
  ];

  // kai-submit fires when the user sends.
  chat.addEventListener('kai-submit', (e) => {
    const userMessage = { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: e.detail.value }] };
    chat.messages = [...chat.messages, userMessage];
    // Call your model here, then append an assistant message.
  });
</script>
```

From that example:

- `messages` is a property. Assign a **new array** to trigger a re-render — mutating in place won't.
- `kai-submit` carries the input on `e.detail.value`, and any staged files on `e.detail.attachments`.
- Give the element an explicit height. It fills its block, scrolling and all.

## Stream a reply

`<kai-chat>` is transport-agnostic: it renders whatever you hand it. Two imports do the streaming: `createAssistantStream` owns the in-flight assistant message, `readOpenAIStream` parses the SSE onto it.

```js

chat.addEventListener('kai-submit', async (e) => {
  const userText = e.detail.value.trim();
  if (!userText) return;

  // 1. Show the user's message.
  const history = [
    ...chat.messages,
    { id: crypto.randomUUID(), role: 'user', parts: [{ type: 'text', text: userText }] },
  ];
  chat.messages = history;
  chat.loading = true;

  // 2. Stream from your endpoint (or a proxy to your model provider).
  const stream = createAssistantStream((update) => {
    chat.messages = update(chat.messages);
  });

  try {
    const res = await fetch('/api/chat', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ messages: toOpenAIMessages(history) }),
    });
    await readOpenAIStream(res, stream);
  } finally {
    stream.done();
    chat.loading = false;
  }
});
```

That covers text, reasoning and tool calls. For the tool loop, Anthropic streams and the error model, see the [wire adapter recipe](/guides/recipes/wire-adapter/).

## In your framework

The element behaves the same everywhere. Only the binding syntax differs.

<Tabs>
<TabItem label="HTML">
```html
<kai-chat id="chat" style="display:block; height:100dvh;"></kai-chat>

<script type="module">

  await customElements.whenDefined('kai-chat');

  const chat = document.getElementById('chat');
  chat.messages = [{ id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello!' }] }];
  chat.addEventListener('kai-submit', (e) => console.log(e.detail.value));
</script>
```
</TabItem>
<TabItem label="React">
```tsx

export function App() {
  const messages: ChatMessage[] = [
    { id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello!' }] },
  ];
  return (
    <Chat
      messages={messages}
      style={{ display: 'block', height: '100dvh' }}
      onSubmit={(e) => console.log(e.detail.value)}
    />
  );
}
```
</TabItem>
<TabItem label="Vue">
```vue
<template>
  <kai-chat
    :messages.prop="messages"
    style="display:block; height:100dvh;"
    @kai-submit="onSubmit"
  />
</template>

<script setup>

const messages = [{ id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello!' }] }];
const onSubmit = (e) => console.log(e.detail.value);
</script>
```
</TabItem>
<TabItem label="Svelte">
```svelte
<script>

  const messages = [{ id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello!' }] }];
</script>

<kai-chat
  {messages}
  style="display:block; height:100dvh;"
  on:kai-submit={(e) => console.log(e.detail.value)}
></kai-chat>
```
</TabItem>
<TabItem label="Angular">
```html
<kai-chat
  [messages]="messages"
  style="display:block; height:100dvh;"
  (kai-submit)="onSubmit($event)"
></kai-chat>
```
</TabItem>
<TabItem label="SolidJS">
```tsx
// Solid apps can compose the native components instead of the element.

function App() {
  const [input, setInput] = createSignal('');

  return (
    <ChatConfig>
      <ChatContainer style={{ height: '100dvh' }}>
        <ChatContainerContent>
          <Message>
            <MessageContent markdown>Hello! How can I help?</MessageContent>
          </Message>
        </ChatContainerContent>
      </ChatContainer>
      <PromptInput
        value={input()}
        onValueChange={setInput}
        onSubmit={() => {
          console.log(input());
          setInput('');
        }}
      >
        
        <PromptInputActions>
          <Button size="sm" disabled={!input()}>Send</Button>
        </PromptInputActions>
      </PromptInput>
    </ChatConfig>
  );
}
```
</TabItem>
</Tabs>

> **note:** 
Arrays and objects — `messages`, `models`, `context`, `attachments` — must be set in JavaScript, never as HTML attributes (an attribute is always a string). Scalars like `placeholder`, `loading`, and `theme` are fine as attributes. Each framework's property binding handles this for you; the gotcha only bites raw HTML.

## What's next

- **[Frameworks](/guides/frameworks/overview/)** — idiomatic wiring for React, Vue, Angular, Svelte, and SolidJS.
- **[Components](/components/chat/)** — full props, events, and examples for `kai-chat` and every other element.
