Package Exports
- ai-input-react
- ai-input-react/styles.css
Readme
ai-input-react
A React input component with AI text/voice support. Unified text and audio input with real-time waveform visualization, designed for AI-powered applications.
Why Use This?
- 🎤 Unified Input – Text and audio in a single component
- 🌊 Real-time Waveform – Audio visualization during recording
- 🎨 Zero Config Styling – Prepacked CSS, no Tailwind needed
- 🔌 Headless Mode – Full control with render props
- ⚡ Framework Agnostic – Next.js, Vite, Laravel, etc.
Installation
# npm
npm install ai-input-react
# yarn
yarn add ai-input-react
# pnpm
pnpm add ai-input-reactQuick Start
import { AiInput } from 'ai-input-react'
import 'ai-input-react/styles.css'
function App() {
return (
<AiInput
send={async (input) => {
const res = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ message: input }),
})
return res.json()
}}
onSuccess={(result) => console.log('Response:', result)}
/>
)
}That's it! The component includes text input with a microphone button for audio recording.
Framework Examples
Next.js (App Router)
// app/page.tsx
'use client'
import { AiInput } from 'ai-input-react'
import 'ai-input-react/styles.css'
export default function Home() {
return (
<AiInput
send={async (input) => {
const res = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ message: input }),
})
return res.json()
}}
/>
)
}Laravel + Inertia
// resources/js/Pages/Chat.tsx
import { AiInput } from 'ai-input-react'
import 'ai-input-react/styles.css'
export default function Chat({ csrfToken }: { csrfToken: string }) {
return (
<AiInput
send={async (input) => {
const res = await fetch('/api/chat', {
method: 'POST',
headers: {
'X-CSRF-TOKEN': csrfToken,
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: input }),
})
return res.json()
}}
/>
)
}Vite
// src/App.tsx
import { AiInput } from 'ai-input-react'
import 'ai-input-react/styles.css'
export default function App() {
return (
<AiInput
send={async (input) => {
const res = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ message: input }),
})
return res.json()
}}
/>
)
}GPT + Whisper Example
<AiInput
placeholder="Ask anything..."
send={async (input) => {
// Text → GPT
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-4',
messages: [{ role: 'user', content: input as string }],
}),
})
return response.json()
}}
sendAudio={async (blob) => {
// Audio → Whisper
const formData = new FormData()
formData.append('file', blob, 'audio.webm')
formData.append('model', 'whisper-1')
const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: formData,
})
return response.json()
}}
onTranscription={(text) => console.log('Transcribed:', text)}
/>API Reference
Props
| Prop | Type | Required | Description |
|---|---|---|---|
send |
(input: string | Blob) => Promise<any> |
✅ | Transport function for sending input |
sendAudio |
(blob: Blob) => Promise<any> |
Separate transport for audio (uses send if not provided) |
|
placeholder |
string |
Input placeholder text | |
disabled |
boolean |
Disable the input | |
className |
string |
Additional CSS classes | |
rateLimit |
{ cooldownMs, maxRequests, windowMs } |
Rate limiting configuration | |
audioConfig |
{ maxDurationMs, mimeTypes } |
Audio recording settings | |
onSuccess |
(result: any) => void |
Called on successful response | |
onError |
(error: Error) => void |
Called on error | |
onTranscription |
(text: string) => void |
Called when audio is transcribed | |
children |
(props: RenderProps) => ReactNode |
Render prop for headless usage |
Render Props (Headless Mode)
<AiInput send={sendFn}>
{(props) => (
// Full control over UI
)}
</AiInput>| Prop | Type | Description |
|---|---|---|
text |
string |
Current text value |
setText |
(value: string) => void |
Update text |
submit |
() => void |
Submit current input |
canSubmit |
boolean |
Whether submit is allowed |
state |
'idle' | 'loading' | 'success' | 'error' | 'recording' |
Current state |
isRecording |
boolean |
Audio recording active |
startRecording |
() => Promise<void> |
Start recording |
stopRecording |
() => void |
Stop and send recording |
cancelRecording |
() => void |
Discard recording |
audioLevels |
number[] |
Waveform data (0-1) |
recordingDuration |
number |
Recording time in ms |
error |
Error | null |
Current error |
reset |
() => void |
Reset to idle state |
Styling
Prepacked CSS (Recommended)
import 'ai-input-react/styles.css'Includes dark theme with zinc/amber colors, waveform visualization, and smooth animations.
Custom Styling (Tailwind)
If using Tailwind, don't import the CSS file. The component uses Tailwind utility classes that will be processed by your build.
Security
⚠️ Never store API keys in frontend code!
Use short-lived tokens from your backend:
// ❌ Dangerous
const API_KEY = 'sk-...'
// ✅ Safe
const token = await getTokenFromBackend()Browser Support
| Browser | Version |
|---|---|
| Chrome | 49+ |
| Firefox | 36+ |
| Safari | 14.1+ |
| Edge | 79+ |
Audio recording requires HTTPS (or localhost) and microphone permission.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Issues & Support
- 🐛 Bug Reports: Open an issue
- 💡 Feature Requests: Open an issue
- 💬 Questions: GitHub Discussions
License
MIT © 2024