Build a composer
<kai-prompt-input> ships a textarea and a Send button. Everything else — the + menu, a model switcher, an effort selector, voice buttons, suggestion chips — you compose in through its toolbar slots. Here’s the full Claude-style composer, built from kit pieces. Open the + menu, switch models, pick an effort, or click a starter.
The card exposes three slots — input-top, toolbar-start, and toolbar-end — all inside its shadow boundary. Anything you want above or below the whole card (the notice, the chips) is your own layout: a sibling element, not a slot. We build it in that order.
Start with the input
Section titled “Start with the input”Register the elements once, then drop the card in. submit="auto" hides the Send button until there’s text; attach={false} (the attach attribute set to false) drops the built-in paperclip so you can supply your own attach action from the + menu.
<script type="module"> import '@kitn.ai/ui/elements';</script>
<kai-prompt-input placeholder="How can I help you today?" attach="false" submit="auto"></kai-prompt-input>Listen for kai-submit on the element itself — kit events don’t bubble. event.detail.value is the flattened text.
const input = document.querySelector('kai-prompt-input');input.addEventListener('kai-submit', (e) => { console.log('send:', e.detail.value);});Add the + menu in toolbar-start
Section titled “Add the + menu in toolbar-start”toolbar-start is the leading region of the toolbar — the conventional home for a + attach menu. Project a <kai-menu> there. Give it trigger-icon="plus" for the built-in icon button and a label so the icon-only trigger has an accessible name.
<kai-prompt-input placeholder="How can I help you today?" attach="false" submit="auto"> <kai-menu slot="toolbar-start" trigger-icon="plus" label="Add"></kai-menu></kai-prompt-input>The menu’s items is an array, so set it in JavaScript (arrays can’t be HTML attributes). Nest items for a submenu, add { separator: true } for a rule, and checked for a toggle. The kai-select event carries { id } (plus checked for toggle items).
const plus = document.querySelector('kai-menu[slot="toolbar-start"]');plus.items = [ { id: 'add-files', label: 'Add files or photos', icon: 'paperclip', shortcut: '⌘U' }, { id: 'add-github', label: 'Add from GitHub', icon: 'github' }, { label: 'Skills', icon: 'sparkles', items: [ { id: 'skill-creator', label: 'skill-creator', icon: 'sparkles' }, { id: 'manage-skills', label: 'Manage skills', icon: 'settings' }, ], }, { separator: true }, { id: 'web-search', label: 'Web search', icon: 'globe', checked: false },];
plus.addEventListener('kai-select', (e) => { console.log('menu:', e.detail.id);});Add the trailing cluster in toolbar-end
Section titled “Add the trailing cluster in toolbar-end”toolbar-end is the trailing region, just before the Send button. The model switcher, an effort menu, and the two voice buttons live here. Wrap them in a plain <div slot="toolbar-end"> — one slot holds the whole group, and your wrapper lays them out.
<kai-prompt-input placeholder="How can I help you today?" attach="false" submit="auto"> <kai-menu slot="toolbar-start" trigger-icon="plus" label="Add"></kai-menu>
<div slot="toolbar-end" style="display:flex; align-items:center; gap:.25rem"> <kai-model-switcher></kai-model-switcher> <kai-menu trigger-label="High" trigger-icon-trailing="chevron-down"></kai-menu> <kai-button variant="subtle" size="icon-sm" icon="mic" label="Voice input"></kai-button> <kai-button variant="subtle" size="icon-sm" icon="audio-lines" label="Voice mode"></kai-button> </div></kai-prompt-input>Wire the model switcher and the effort menu the same way — array data in JavaScript, events on the element. The model switcher fires kai-model-change; the effort menu reuses kai-select.
const models = document.querySelector('kai-model-switcher');models.models = [ { id: 'opus-4-8', name: 'Opus 4.8' }, { id: 'sonnet', name: 'Sonnet' }, { id: 'haiku', name: 'Haiku' },];models.currentModel = 'opus-4-8';models.addEventListener('kai-model-change', (e) => console.log('model:', e.detail));
const effort = document.querySelector('div[slot="toolbar-end"] kai-menu');effort.items = [ { heading: true, label: 'Effort' }, { id: 'high', label: 'High' }, { id: 'medium', label: 'Medium' }, { id: 'low', label: 'Low' },];effort.addEventListener('kai-select', (e) => console.log('effort:', e.detail.id));The mic and voice buttons are plain <kai-button>s — variant + icon do the styling, and label is required because they’re icon-only. Listen for kai-click to wire them up. <kai-prompt-input> also has built-in voice and search props if you’d rather use its own toolbar buttons; here we compose our own so they sit in the trailing cluster.
Suggestions and the notice — your own layout
Section titled “Suggestions and the notice — your own layout”The chips below the card and the notice above it aren’t slots — there’s no region for them inside the card’s shadow boundary. They’re sibling elements in your layout. Stack them around the input:
<div style="display:flex; flex-direction:column; gap:.75rem; max-width:640px"> <kai-notice severity="warning" dismissible> A scheduled maintenance window starts at 18:00 UTC. <a slot="action" href="/status">Status</a> </kai-notice>
<kai-prompt-input placeholder="How can I help you today?" attach="false" submit="auto"> <!-- toolbar-start + toolbar-end slots, as above --> </kai-prompt-input>
<kai-suggestions variant="outline" size="sm"></kai-suggestions></div><kai-notice> takes a severity, an optional action slot for a link, and dismissible for the × (which fires kai-dismiss). <kai-suggestions> takes its chips in JavaScript; clicking one fires kai-select.
const suggestions = document.querySelector('kai-suggestions');suggestions.suggestions = [ { label: 'Write', icon: 'pencil' }, { label: 'Learn', icon: 'book-open' }, { label: 'Code', icon: 'code' }, { label: 'Life stuff', icon: 'smile' },];suggestions.addEventListener('kai-select', (e) => { document.querySelector('kai-prompt-input').value = e.detail.value ?? e.detail.label;});- Compose your own shell — step down from
<kai-chat>and assemble the sidebar, thread, and composer for full layout control. kai-prompt-inputreference — every prop, event, slot, and::part(including hiding Send via::part(send)).kai-menureference andkai-model-switcherreference — the menu item tree and the model/group options.