← All components Composites
ChatComposer
The full Cursor/Claude-style composer: model picker + mode tabs + attachments + slash + voice + ⌘↵.
Inspired by Cursor & Claude.ai composers; Anthropic Console.
Preview
What it looks like.
Live — the real component, scoped to this frame.
Usage
Drop it into your app.
import { ChatComposer } from '@locara/components'
const [text, setText] = useState('')
const [model, setModel] = useState('qwen2.5-7b')
<ChatComposer
value={text}
onChange={setText}
onSubmit={() => llm.chat({ model, messages: [...history, { role: 'user', content: text }] })}
models={[
{ id: 'qwen2.5-7b', label: 'Qwen 2.5 7B', hint: '4.9 GB · Q4_K_M', badges: ['fast'] },
{ id: 'llama-3-70b', label: 'Llama 3 70B', hint: '42.8 GB', badges: ['heavy'], status: 'needs-download', sizeLabel: '42.8 GB' },
]}
modelId={model}
onModelChange={setModel}
modes={[{ id: 'ask', label: 'Ask' }, { id: 'edit', label: 'Edit' }, { id: 'agent', label: 'Agent' }]}
modeId="edit"
onModeChange={() => {}}
attachments={[{ id: '1', name: 'design-system.pdf', kind: 'pdf', sizeLabel: '2.1 MB' }]}
onAttachClick={() => filePicker.open()}
onVoiceClick={() => voice.start()}
slashCommands={[
{ id: 'rewrite', label: 'Rewrite', description: 'Improve grammar and clarity' },
{ id: 'translate', label: 'Translate', description: 'To another language' },
]}
/>
Or copy the source into your repo:
locara add chat-composer
Design notes
Why it works this way.
ChatComposer is intentionally batteries-included. Every sub-feature is optional — pass only `value/onChange/onSubmit` for a plain composer; layer on the model picker, mode tabs, attachments rail, slash menu, voice button as your app needs them. The ⌘↵ shortcut is baked in. Slash-command detection re-uses the underlying SlashMenu component so it shares a single keyboard model with anything else that uses slash menus.