AI chat
Chat input
The composer: an auto-growing textarea, an attachment slot, and a send button.
Client componentSource
Import
import { ChatInput } from '@the_viveksingh/vivek-ui'A composer
Enter sends, Shift+Enter adds a line, and the box grows to maxRows before it scrolls. onSubmit receives the trimmed draft and is never called while empty, disabled or busy.
<ChatInput
label="Message"
hideLabel
placeholder="Ask anything. Enter sends, Shift+Enter adds a line."
maxRows={6}
onSubmit={send}
/>With attachments
The attachments slot sits above the box and takes any node.
<ChatInput
label="Message"
hideLabel
attachments={
<Stack direction="horizontal" gap={2} wrap>
{files.map((file) => (
<Badge key={file.name} tone="neutral">{file.name}</Badge>
))}
</Stack>
}
onSubmit={send}
/>While a request is in flight
busy disables sending, so a second Enter cannot fire the same request twice - the same reason Button has loading.
<ChatInput busy label="Message" hideLabel hint="Waiting for a reply..." />Props
Generated from the package's own type declarations, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled draft text. Pair with `onValueChange`. |
defaultValue | string | — | Initial draft text while uncontrolled. |
onValueChange | (value: string) => void | — | Called on every keystroke, and with an empty string when a send clears the box. |
onSubmit | (value: string) => void | — | Called with the trimmed draft on send. Never called while empty, disabled or busy. |
placeholder | string | — | — |
maxRows | number | — | Rows the box may grow to before it starts scrolling. Defaults to 8. |
minRows | number | — | Rows the box starts at. Defaults to 1. |
disabled | boolean | — | — |
busy | boolean | — | A reply is in flight: the send button spins and Enter will not submit. |
attachments | ReactNode | — | Slot above the box for attachment chips, file pickers, model pickers. |
submitLabel | string | — | Send button text. Defaults to `Send`. |
label | string | — | Textarea label. A real `<label>`, visually hidden unless `hideLabel` is `false`. |
hideLabel | boolean | — | Keep the label off screen. Defaults to `true`. |
hint | ReactNode | — | The `aria-describedby` hint that makes Enter-to-send discoverable. Pass `null` to drop it entirely (then say it somewhere else - do not simply remove it). |
clearOnSubmit | boolean | — | Empty the box after a successful send. Defaults to `true`. |
textareaProps | Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'value' | 'defaultValue' | 'onChange' | 'onKeyDown' | 'id' | 'disabled' | 'placeholder'> | — | Escape hatch onto the textarea itself. |
textareaRef | Ref<HTMLTextAreaElement> | — | Ref to the textarea. The forwarded ref goes to the `<form>` root. |
Rendering
Client component
ChatInput declares 'use client' because it needs state, effects or event handlers. Importing it into a Server Component creates a client boundary at this component — everything above it stays on the server.