Forms
Chat composer
A multi-line message input that grows with the text: Enter sends, Shift+Enter adds a line.
import { ChatComposer } from '@jsonresume/ui'Source(opens in a new tab)
Examples
Show code
chat-composer/copilot.tsx
'use client';
import { Button, ChatComposer, Stack, Text } from '@jsonresume/ui';
import { Mic } from 'lucide-react';
import { useState } from 'react';
export default function ChatComposerCopilot() {
const [sent, setSent] = useState<string[]>([]);
const [busy, setBusy] = useState(false);
return (
<Stack gap={3}>
{sent.map((m, i) => (
<Text key={i} tone="muted" size="xs">
You: {m}
</Text>
))}
<ChatComposer
busy={busy}
hint="Enter to send · Shift+Enter for a new line"
actions={
<Button variant="ghost" size="icon" aria-label="Speak your message">
<Mic aria-hidden="true" />
</Button>
}
onSubmit={(m) => {
setSent((all) => [...all, m]);
setBusy(true);
setTimeout(() => setBusy(false), 1200);
}}
/>
</Stack>
);
}Accessibility
- The textarea is labelled; the Send button shows progress while a reply streams.
- Enter does not send during IME composition.
Props
<ChatComposer>
| Prop | Type | Default | Description |
|---|---|---|---|
| onSubmit required | (message: string) => void | – | |
| actions | ReactNode | – | Extra controls before Send, e.g. a voice input button. |
| busy | boolean | false | While a reply streams: the Send button shows progress and Enter won't send. |
| hint | ReactNode | – | |
| label | string | Message | |
| placeholder | string | Ask anything about your resume… | |
| sendLabel | string | Send |