AI Chat Input
Loading...
A specialized input component designed for AI chat interfaces that combines a textarea with a send button, loading states, and expandable interactions. The component supports two distinct appearances: a standard default mode and an expandable mode that collapses when empty and features animated rotating placeholders.
Overview
Resources
Install
yarn add @camp/ai-chat-inputVariations
Default appearance
The standard chat input appearance provides a consistent, always-visible textarea with a send button.
import { useState } from 'react';
import { AiChatInput } from '@camp/ai-chat-input';
function ChatComponent() {
const [message, setMessage] = useState('');
const handleSendMessage = (e) => {
e.preventDefault();
// Handle sending message
console.log('Sending:', message);
setMessage('');
};
return (
<AiChatInput
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={handleSendMessage}
placeholder="What can I help you with?"
/>
);
}Expandable appearance
The expandable appearance creates a compact, single-line input that expands when focused or clicked. This mode features a gradient border when expanded and automatically collapses when empty and unfocused.
import { useState } from 'react';
import { AiChatInput } from '@camp/ai-chat-input';
function ChatComponent() {
const [message, setMessage] = useState('');
const [isExpanded, setIsExpanded] = useState(false);
const handleSendMessage = (e) => {
e.preventDefault();
console.log('Sending:', message);
setMessage('');
};
return (
<AiChatInput
appearance="expandable"
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={handleSendMessage}
placeholder={[
'Create a campaign for my new product launch',
'Generate a blog post about...',
'Summarize this document...',
'Write code for...',
'Explain the concept of...',
]}
placeholderInterval={2000}
isExpanded={isExpanded}
onExpandedChange={setIsExpanded}
/>
);
}Expandable with rotating placeholder
When using the expandable appearance, you can provide an array of placeholder strings that will automatically rotate at a specified interval. This creates an engaging, dynamic placeholder experience that suggests various use cases to users.
import { useState } from 'react';
import { AiChatInput } from '@camp/ai-chat-input';
function ChatComponent() {
const [message, setMessage] = useState('');
const [isExpanded, setIsExpanded] = useState(false);
const handleSendMessage = (e) => {
e.preventDefault();
console.log('Sending:', message);
setMessage('');
};
return (
<AiChatInput
appearance="expandable"
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={handleSendMessage}
placeholder={[
'Create a campaign for my new product launch',
'Generate a blog post about...',
'Summarize this document...',
'Write code for...',
'Explain the concept of...',
]}
placeholderInterval={2000}
isExpanded={isExpanded}
onExpandedChange={setIsExpanded}
/>
);
}Expandable with custom placeholder interval
Control the speed of placeholder rotation by adjusting the placeholderInterval prop (in milliseconds).
import { useState } from 'react';
import { AiChatInput } from '@camp/ai-chat-input';
function ChatComponent() {
const [message, setMessage] = useState('');
const [isExpanded, setIsExpanded] = useState(false);
return (
<AiChatInput
appearance="expandable"
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={(e) => e.preventDefault()}
placeholder={[
'Fast rotation: 1 second',
'Create a campaign...',
'Generate content...',
'Summarize document...',
]}
placeholderInterval={1000}
isExpanded={isExpanded}
onExpandedChange={setIsExpanded}
/>
);
}Expandable with action buttons
Add custom action buttons that appear when the input expands, such as attachment or formatting controls.
import { useState } from 'react';
import { AiChatInput } from '@camp/ai-chat-input';
function ChatComponent() {
const [message, setMessage] = useState('Test message with actions');
const [isExpanded, setIsExpanded] = useState(true);
const renderActions = () => (
<>
<button
type="button"
style={{
background: 'transparent',
border: '1px solid #ccc',
borderRadius: '8px',
padding: '8px 12px',
cursor: 'pointer',
}}
>
+ Add
</button>
<button
type="button"
style={{
background: 'transparent',
border: '1px solid #ccc',
borderRadius: '8px',
padding: '8px 12px',
cursor: 'pointer',
}}
>
🔗 Connect
</button>
</>
);
return (
<AiChatInput
appearance="expandable"
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={(e) => e.preventDefault()}
placeholder={[
'Ask me anything...',
'What would you like to know?',
'How can I help you today?',
]}
renderActions={renderActions}
isExpanded={isExpanded}
onExpandedChange={setIsExpanded}
/>
);
}Disabled state
Prevent interaction with the input using the disabled prop.
import { useState } from 'react';
import { AiChatInput } from '@camp/ai-chat-input';
function ChatComponent() {
const [message, setMessage] = useState('This input is disabled');
return (
<AiChatInput
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={(e) => e.preventDefault()}
disabled={true}
/>
);
}With accessibility label
Provide an explicit label for screen readers and improved accessibility.
import { useState } from 'react';
import { AiChatInput } from '@camp/ai-chat-input';
function ChatComponent() {
const [message, setMessage] = useState('');
const handleSendMessage = (e) => {
e.preventDefault();
console.log('Sending:', message);
setMessage('');
};
return (
<AiChatInput
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={handleSendMessage}
label="Chat message input"
placeholder="Type your message..."
/>
);
}Companion components
The AI Chat Input includes companion components designed for creating complete, polished chat experiences, particularly when using the expandable appearance.
AiChatInputBackground
A wrapper component that provides a gradient background and fade effect, ideal for hero sections or prominent chat interfaces.
AiChatInputHeader
A heading component with gradient text styling that complements the AI Chat Input aesthetic.
AiChatInputTextButton
A text-style button that matches the AI Chat Input design system, useful for secondary actions like “Start from scratch” or “View examples”.
Complete composition example
import { useState } from 'react';
import {
AiChatInput,
AiChatInputBackground,
AiChatInputHeader,
AiChatInputTextButton,
} from '@camp/ai-chat-input';
function ChatInterface() {
const [message, setMessage] = useState('Create a marketing campaign for my new product');
const [isExpanded, setIsExpanded] = useState(true);
const handleSendMessage = (e) => {
e.preventDefault();
console.log('Sending:', message);
setMessage('');
};
return (
<AiChatInputBackground>
<AiChatInputHeader>Generate an automation in seconds</AiChatInputHeader>
<AiChatInput
appearance="expandable"
value={message}
onChange={(e) => setMessage(e.target.value)}
onSendMessage={handleSendMessage}
placeholder={[
'Create a campaign for my new product launch',
'Generate a blog post about...',
'Summarize this document...',
]}
isExpanded={isExpanded}
onExpandedChange={setIsExpanded}
/>
<AiChatInputTextButton onClick={() => console.log('Starting from scratch')}>
Start from scratch instead
</AiChatInputTextButton>
</AiChatInputBackground>
);
}Usage
Best practices
Appearance selection
- Use the default appearance for chat interfaces where the input should always be visible and prominent
- Use the expandable appearance for hero sections, landing pages, or interfaces where space is limited and you want an elegant, minimalist design
Placeholder strategies
- For default appearance, use a single, clear placeholder string that explains the input’s purpose
- For expandable appearance, consider using an array of placeholders to showcase different use cases and inspire users
- Keep placeholder text concise (under 60 characters) to ensure it fits within the collapsed state
- Rotate placeholders every 2-4 seconds for optimal readability without distraction
State management
- Always use controlled components with
valueandonChangeprops - Manage the
isExpandedstate externally when you need to programmatically control the expansion - Set
isLoadingtotrueimmediately when sending messages to prevent duplicate submissions - Clear the input value after successful message submission
Loading and feedback
- Use the
isLoadingstate to provide visual feedback when processing messages - The component automatically disables the input and send button during loading
- Consider showing additional feedback (toast notifications, response displays) outside the component
Accessibility
- Provide the
labelprop for screen reader context, especially when the input’s purpose isn’t clear from surrounding content - The component handles keyboard navigation (Enter to send, Shift+Enter for new line) automatically
- Ensure sufficient color contrast in custom themes
Advanced features
- Use
renderActionsto add contextual actions (attachments, formatting) that appear when the input expands - Set
disabledto prevent interaction during critical operations or when prerequisites aren’t met - Use
hideSendButtononly when you have clear alternative submission UI or rely entirely on Enter key submission
Content guidelines
✅ DO
Use clear, action-oriented placeholder text: “Create a campaign for…”, “Ask me anything…”, “What can I help you with?”
🚫 DON’T
Use vague or technical placeholder text: “Input text here”, “Enter query”, “Type stuff”
✅ DO
Provide 3-5 varied placeholder examples in rotating arrays that showcase different use cases
🚫 DON’T
Use 10+ placeholder variations or repeat similar phrasing across placeholders
Accessibility
Keyboard support
- Enter: Submits the message (triggers
onSendMessage) - Shift + Enter: Inserts a new line in the textarea
- Tab: Moves focus between the textarea, action buttons (if present), and send button
- The component automatically manages focus states during expand/collapse transitions
Screen reader support
- The textarea includes a
textboxrole for proper identification - The
labelprop adds an associated<label>element for improved context - The send button includes descriptive text (“Send”) for screen readers
- Loading states disable the input with appropriate semantic attributes
- Placeholder text is accessible to screen readers when the input is empty
Focus management
- The expandable appearance automatically focuses the textarea when clicked or expanded
- Focus remains within the component when tabbing between textarea and action buttons
- The component prevents focus when disabled or loading
- Blur behavior is intelligent: the expandable variant collapses only when truly losing focus and empty
Best practices for accessibility
- Always provide the
labelprop when the input’s purpose isn’t clear from surrounding content - Ensure placeholder text is meaningful and not the only way to understand the input’s purpose
- Don’t rely solely on visual loading indicators; the component’s disabled state provides semantic feedback
- Test keyboard navigation thoroughly, especially with custom
renderActionsimplementations - Maintain sufficient color contrast ratios in custom themes (component uses design tokens by default)