ChatInput
ChatInput is a multi-part textarea component built for AI chat interfaces — auto-resizing input, send/stop button, and loading state managed as a single unit.
Playground
<ChatInput onSubmit={(value) => console.log(value)}>
<ChatInputField placeholder="Ask me anything..." />
<ChatInputSubmit />
</ChatInput>Variants
Sizes
smdefaultlg| Size | Button | Font | Padding | Preview | |
|---|---|---|---|---|---|
sm | 28px | 14px | 10px / 12px | ||
default | 32px | 14px | 12px / 16px | ||
lg | 36px | 16px | 16px / 20px | ||
Features
Auto Resize
The textarea grows with content and shrinks when lines are removed — bound by maxRows.
<ChatInput onSubmit={(value) => console.log(value)}>
<ChatInputField
placeholder="Type multiple lines (Shift+Enter for newline)..."
maxRows={5}
/>
<ChatInputSubmit />
</ChatInput>Character Count
Combine showCount with maxLength to display a live character counter in the bottom-right corner.
<ChatInput onSubmit={(value) => console.log(value)}>
<ChatInputField
placeholder="Ask me anything..."
showCount
maxLength={500}
/>
<ChatInputSubmit />
</ChatInput>Loading State
Set loading=true to switch the button to a stop icon — button stays enabled so users can abort.
<ChatInput onSubmit={(value) => console.log(value)}>
<ChatInputField placeholder="Response generating..." />
<ChatInputSubmit loading onStop={() => console.log('stopped')} />
</ChatInput>Filled Variant
variant="filled" uses a muted background with no border, ideal for chat surfaces where the input should recede.
<ChatInput variant="filled" onSubmit={(value) => console.log(value)}>
<ChatInputField placeholder="Ask me anything..." />
<ChatInputSubmit />
</ChatInput>Disabled
disabled disables both input and submit together — communicated visually with reduced opacity.
<ChatInput disabled onSubmit={(value) => console.log(value)}>
<ChatInputField placeholder="Currently unavailable..." />
<ChatInputSubmit />
</ChatInput>Customization
Replace the default send icon via the children prop on ChatInputSubmit.
Custom Icon
<ChatInputSubmit>
<ArrowUp className="icon-xs" />
</ChatInputSubmit>Voice Input Style
<ChatInputSubmit>
<Mic className="icon-xs" />
</ChatInputSubmit>API
Component Structure
ChatInput— Compound · Pure ReactChatInputContext provider + containerChatInputFieldAuto-resizing textareaChatInputSubmitSend / stop buttonMulti-part component — ChatInput (root), ChatInputField (textarea), ChatInputSubmit (send button).
Props
ChatInput
variant"outline""outline" | "filled"Visual style — outline (bordered) or filled (muted background)
size"default""sm" | "default" | "lg"Size scale — sm, default, or lg
radius"xl""sm" | "md" | "lg" | "xl" | "2xl" | "full"Container corner radius
color"default""default" | "primary"Submit button color — default (dark) or primary (brand)
layout"default""default" | "inline"Layout direction — default (stacked) or inline (field + button side by side)
disabledfalsebooleanDisable both input and submit together
onSubmitundefined(value: string) => voidSubmit callback — receives the current text value
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
variant | "outline" | "filled" | "outline" | Visual style — outline (bordered) or filled (muted background) |
size | "sm" | "default" | "lg" | "default" | Size scale — sm, default, or lg |
radius | "sm" | "md" | "lg" | "xl" | "2xl" | "full" | "xl" | Container corner radius |
color | "default" | "primary" | "default" | Submit button color — default (dark) or primary (brand) |
layout | "default" | "inline" | "default" | Layout direction — default (stacked) or inline (field + button side by side) |
disabled | boolean | false | Disable both input and submit together |
onSubmit | (value: string) => void | undefined | Submit callback — receives the current text value |
className | string | undefined | Additional CSS classes |
ChatInputField
maxRows8numberMaximum visible rows before the field scrolls
showCountfalsebooleanShow character counter — requires maxLength
maxLengthundefinednumberMaximum character count
placeholderundefinedstringPlaceholder text shown when empty
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
maxRows | number | 8 | Maximum visible rows before the field scrolls |
showCount | boolean | false | Show character counter — requires maxLength |
maxLength | number | undefined | Maximum character count |
placeholder | string | undefined | Placeholder text shown when empty |
className | string | undefined | Additional CSS classes |
ChatInputSubmit
loadingfalsebooleanSwitch to stop icon — button stays enabled
onStopundefined() => voidCalled when clicked in loading state
buttonRadiusundefined"sm" | "md" | "lg" | "xl" | "2xl" | "full"Override button corner radius — auto-calculated from container radius when omitted
childrenundefinedReactNodeCustom icon — defaults to send icon
classNameundefinedstringAdditional CSS classes
| Name | Type | Default | Description |
|---|---|---|---|
loading | boolean | false | Switch to stop icon — button stays enabled |
onStop | () => void | undefined | Called when clicked in loading state |
buttonRadius | "sm" | "md" | "lg" | "xl" | "2xl" | "full" | undefined | Override button corner radius — auto-calculated from container radius when omitted |
children | ReactNode | undefined | Custom icon — defaults to send icon |
className | string | undefined | Additional CSS classes |
Anatomy
Best Practices
Recommended
- ✓Manage loading state in onSubmit for async sends
- ✓Set maxRows to prevent unbounded growth
- ✓Use a clear placeholder to indicate purpose
Don't
- ✗Don't mutate field value outside onSubmit in uncontrolled mode
- ✗Don't combine loading and disabled — they conflict
- ✗Don't use for general-purpose forms — use Textarea instead
Accessibility
Keyboard
ARIA / WCAG
- textarea element — screen readers announce correctly
- Submit button has automatic aria-label (Send / Stop)
- Disabled state is visually and semantically distinct