Getting Started
Components
ButtonGroupInputGroup
A comprehensive input component with text, file upload, and voice recording capabilities.
"use client"
import * as React from "react"
import { ButtonGroupInputGroup } from "@/components/ui/button-group-input-group"
export function ButtonGroupInputGroupDemo() {
const [messages, setMessages] = React.useState<
Array<{ type: string; content: string; timestamp: Date }>
>([])
const handleSendMessage = (message: string) => {
setMessages((prev) => [
...prev,
{ type: "text", content: message, timestamp: new Date() },
])
}
const handleFileSelect = (file: File) => {
setMessages((prev) => [
...prev,
{ type: "file", content: file.name, timestamp: new Date() },
])
}
const handleAudioRecord = (audioBlob: Blob) => {
setMessages((prev) => [
...prev,
{
type: "audio",
content: `Audio recording (${Math.round(audioBlob.size / 1024)}KB)`,
timestamp: new Date(),
},
])
}
return (
<div className="space-y-4">
<div className="space-y-2">
<h3 className="text-sm font-medium">ButtonGroupInputGroup</h3>
<p className="text-muted-foreground text-xs">
A comprehensive input component with text, file upload, and voice
recording capabilities.
</p>
</div>
<div className="space-y-3">
<ButtonGroupInputGroup
onSendMessage={handleSendMessage}
onFileSelect={handleFileSelect}
onAudioRecord={handleAudioRecord}
/>
{messages.length > 0 && (
<div className="space-y-2">
<div className="text-muted-foreground text-xs font-medium">
Messages:
</div>
<div className="max-h-32 space-y-1 overflow-y-auto">
{messages.map((message, index) => (
<div key={index} className="bg-muted rounded-md p-2 text-xs">
<div className="flex items-center gap-2">
<span className="font-medium capitalize">
{message.type}:
</span>
<span>{message.content}</span>
</div>
<div className="text-muted-foreground mt-1 text-xs">
{message.timestamp.toLocaleTimeString()}
</div>
</div>
))}
</div>
</div>
)}
</div>
</div>
)
}
Installation
pnpm dlx shadcn@latest add button-group-input-group
Usage
import { ButtonGroupInputGroup } from "@/components/ui/button-group-input-group"
<ButtonGroupInputGroup
onSendMessage={(message) => {
console.log("Sending message:", message)
}}
onFileSelect={(file) => {
console.log("File selected:", file.name)
}}
onAudioRecord={(audioBlob) => {
console.log("Audio recorded:", audioBlob)
}}
/>
Features
- Text Input: Standard text input with Enter key support
- File Upload: Click to attach files of any type
- Voice Recording: Record audio messages with start/stop controls
- Voice Mode Toggle: Switch between text and voice input modes
- Keyboard Shortcuts: Enter to send, Shift+Enter for new lines
- Accessibility: Full ARIA support and keyboard navigation
Props
Prop | Type | Default | Description |
---|---|---|---|
onSendMessage | (message: string) => void | undefined | Callback fired when a text message is sent |
onFileSelect | (file: File) => void | undefined | Callback fired when a file is selected |
onAudioRecord | (audioBlob: Blob) => void | undefined | Callback fired when audio recording is completed |
Behavior
Text Mode (Default)
- Users can type messages in the input field
- Press Enter to send the message
- Press Shift+Enter to create a new line
- Send button is disabled when input is empty
- File attachment button is available
Voice Mode
- Click the voice mode button to enable voice recording
- Input field becomes disabled and shows recording instructions
- Microphone button appears for start/stop recording
- File attachment button is disabled during voice mode
- Send button is hidden in voice mode
File Upload
- Click the plus (+) button to open file picker
- Accepts any file type (
accept="*/*"
) - File input is reset after selection
- Disabled during voice mode
Audio Recording
- Requires microphone permissions
- Records in WebM format
- Visual feedback with recording state
- Automatic cleanup of media streams
- Error handling for permission issues
Examples
Basic Usage
<ButtonGroupInputGroup />
With API Integration
<ButtonGroupInputGroup
onSendMessage={async (message) => {
await fetch("/api/messages", {
method: "POST",
body: JSON.stringify({ message }),
})
}}
onFileSelect={async (file) => {
const formData = new FormData()
formData.append("file", file)
await fetch("/api/upload", {
method: "POST",
body: formData,
})
}}
onAudioRecord={async (audioBlob) => {
const formData = new FormData()
formData.append("audio", audioBlob, "recording.webm")
await fetch("/api/audio", {
method: "POST",
body: formData,
})
}}
/>
With State Management
function ChatInput() {
const [messages, setMessages] = useState([])
const [isUploading, setIsUploading] = useState(false)
const handleSendMessage = async (message) => {
setMessages((prev) => [...prev, { type: "text", content: message }])
// Send to backend...
}
const handleFileSelect = async (file) => {
setIsUploading(true)
try {
// Upload logic...
setMessages((prev) => [...prev, { type: "file", content: file.name }])
} finally {
setIsUploading(false)
}
}
return (
<div className="space-y-2">
<ButtonGroupInputGroup
onSendMessage={handleSendMessage}
onFileSelect={handleFileSelect}
/>
{isUploading && <div>Uploading...</div>}
</div>
)
}
Accessibility
- ARIA Labels: Proper labeling for all interactive elements
- Keyboard Navigation: Full keyboard support
- Screen Readers: Descriptive tooltips and state announcements
- Focus Management: Logical tab order
- Color Contrast: Meets WCAG guidelines
Browser Support
- Modern Browsers: Chrome, Firefox, Safari, Edge (latest versions)
- MediaRecorder API: Required for voice recording functionality
- File API: Required for file upload functionality
- HTTPS: Required for microphone access in production
Deploy your aisdk agents app on Vercel
Trusted by OpenAI, Sonos, Adobe, and more.
Vercel provides tools and infrastructure to deploy apps and features at scale.
Deploy to Vercel