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="font-medium text-sm">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
onAudioRecord={handleAudioRecord}
onFileSelect={handleFileSelect}
onSendMessage={handleSendMessage}
/>
{messages.length > 0 && (
<div className="space-y-2">
<div className="font-medium text-muted-foreground text-xs">
Messages:
</div>
<div className="max-h-32 space-y-1 overflow-y-auto">
{messages.map((message, index) => (
<div className="rounded-md bg-muted p-2 text-xs" key={index}>
<div className="flex items-center gap-2">
<span className="font-medium capitalize">
{message.type}:
</span>
<span>{message.content}</span>
</div>
<div className="mt-1 text-muted-foreground 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
Join The Beta
Copy and Paste 42+
ai sdk patterns.
ai sdk patterns.
Get early access pricing and shape the future of the product.
Go to pricing