Skip to content
kitn AI/UI

Frameworks

The kai-* web components drop into React, Vue, Angular, Svelte, or plain HTML with a single import — no per-framework package, no wrappers. React adds a typed adapter; in a Solid app you can import the native components directly.

EntryUse it for
@kitn.ai/ui/elementsAny framework — registers every kai-* element
@kitn.ai/ui/reactReact — typed wrappers with camelCase props and event handlers
@kitn.ai/ui/solidSolidJS — native Solid components for full compositional control
@kitn.ai/uiThe shared layer every framework uses — types, state and card helpers

Import @kitn.ai/ui/elements once for its side effect. It registers every kai-* element; after that they work like built-in HTML.

import '@kitn.ai/ui/elements';

Registration is async. If your framework sets array or object properties as it mounts (Vue, Angular, Svelte all do), mount behind the elementsReady promise the same entry exports. A property written before the element upgrades is lost.

import { elementsReady } from '@kitn.ai/ui/elements';
elementsReady.then(() => mountYourApp());

Then use them in your templates:

<kai-chat style="display:block; height:100%;"></kai-chat>
<script type="module">
import '@kitn.ai/ui/elements';
await customElements.whenDefined('kai-chat');
const chat = document.querySelector('kai-chat');
chat.messages = [{ id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello!' }] }];
chat.addEventListener('kai-submit', (e) => console.log(e.detail));
</script>

The one thing to get right: Arrays and objects must be set in JavaScript, not as HTML attributes — an attribute is always a string, so writing messages or models as an attribute silently does nothing.

const chat = document.querySelector('kai-chat');
// Right: set the array in JavaScript.
chat.messages = [{ id: '1', role: 'assistant', parts: [{ type: 'text', text: 'Hello!' }] }];
// Wrong: an attribute is a string, so the element never gets the array.
// <kai-chat messages="[...]"></kai-chat>

Scalars — placeholder, loading, theme, prose-size — work fine as attributes. Each framework’s binding picks the right path for you: Vue uses :prop.prop, Angular uses [prop], Svelte binds objects directly.

Every kai-* element dispatches CustomEvents with a detail payload, named with a kai- prefix (kai-submit, kai-model-change, kai-remove, …).

chat.addEventListener('kai-submit', (e) => {
const { value } = e.detail;
// send value to your model API
});

The events don’t bubble — listen on the element itself.

@kitn.ai/ui/react exports a typed component for every element. Props take arrays and objects directly (no .prop modifier), and events arrive as camelCase handler props that receive the CustomEvent.

import { Chat } from '@kitn.ai/ui/react';
export function MyChatApp() {
return (
<Chat
messages={messages}
onSubmit={(e) => console.log(e.detail.value)}
onModelChange={(e) => console.log(e.detail)}
/>
);
}

Writing Solid? Skip the element wrapper and compose the kit’s native components from @kitn.ai/ui/solid. That entry is the complete Solid catalog, compiled as its own bundle so the other frameworks never pay for it. It tree-shakes to exactly what you import and mixes freely with your own primitives.

import { createSignal } from 'solid-js';
import {
Button,
ChatConfig,
ChatContainer,
ChatContainerContent,
Message,
MessageContent,
PromptInput,
PromptInputTextarea,
PromptInputActions,
} from '@kitn.ai/ui/solid';
export 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={() => {
handleSubmit(input());
setInput('');
}}
>
<PromptInputTextarea placeholder="Ask anything…" />
<PromptInputActions>
<Button size="sm" disabled={!input()}>Send</Button>
</PromptInputActions>
</PromptInput>
</ChatConfig>
);
}

Solid usage also needs solid-js as a peer dependency:

Terminal window
npm install solid-js

Unlike the elements, the native components are Tailwind v4 classes, so this path also needs Tailwind in the app plus an @source line pointing at the package. Rendering a real thread means MessageBody over each message’s ordered parts, or Thread for the whole list at once. Both are covered in the SolidJS guide.

Full setup, binding patterns, and examples for each environment: