react-voice-to-text-converter
v1.0.3
Published
> A powerful React hook for seamless speech-to-text conversion with real-time transcription, multi-language support, and automatic punctuation.
Readme
react-voice-to-text-converter
A powerful React hook for seamless speech-to-text conversion with real-time transcription, multi-language support, and automatic punctuation.
✨ Features
- 🎤 Real-time Speech Recognition - Live transcription as you speak
- 🌍 Multi-language Support - 12+ languages including English, Spanish, Hindi, Chinese, and more
- 📝 Automatic Punctuation - Voice commands for commas, periods, question marks, etc.
- 🔢 Smart Number Conversion - Converts spoken numbers to digits automatically
- 📋 Append to Existing Text - Preserve and build upon existing content
- ⚡ Continuous Recording - No interruptions during speech
- 🎯 TypeScript Ready - Full type definitions included
- 🚀 Zero Dependencies - Built on native Web Speech API
- 📱 Mobile Friendly - Works on iOS Safari and Android Chrome
📦 Installation
npm install react-voice-to-text-converteror
yarn add react-voice-to-text-converter🚀 Quick Start
import React, { useState, useCallback } from 'react';
import { useVoiceInput } from 'react-voice-to-text-converter';
function App() {
const [text, setText] = useState('');
const handleTranscript = useCallback((transcript, isFinal) => {
setText(transcript);
}, []);
const { isListening, toggleListening, isSupported } = useVoiceInput({
onTranscript: handleTranscript,
language: 'en-US'
});
if (!isSupported) {
return <div>Voice input not supported. Use Chrome, Edge, or Safari.</div>;
}
return (
<div>
<textarea value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={toggleListening}>
{isListening ? '🛑 Stop' : '🎤 Start'} Recording
</button>
</div>
);
}📖 Documentation
API Reference
Parameters
useVoiceInput({
onTranscript: (text: string, isFinal: boolean) => void,
language?: string, // Default: 'en-US'
disabled?: boolean // Default: false
})| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| onTranscript | (text: string, isFinal: boolean) => void | ✅ | - | Callback fired when transcript updates |
| language | string | ❌ | 'en-US' | Language code for recognition |
| disabled | boolean | ❌ | false | Disable voice input functionality |
Return Values
{
isListening: boolean;
isSupported: boolean;
error: string | null;
startListening: () => void;
stopListening: () => void;
toggleListening: () => void;
clearTranscript: () => void;
setBaseText: (text: string) => void;
}| Property | Type | Description |
|----------|------|-------------|
| isListening | boolean | true when actively recording |
| isSupported | boolean | true if browser supports Web Speech API |
| error | string \| null | Error message if recognition fails |
| startListening | function | Start voice recognition |
| stopListening | function | Stop voice recognition |
| toggleListening | function | Toggle recording on/off |
| clearTranscript | function | Clear transcript and stop recording |
| setBaseText | function | Set existing text to append new speech to |
🌍 Supported Languages
const languages = [
'en-US', // English (US)
'en-GB', // English (UK)
'es-ES', // Spanish
'fr-FR', // French
'de-DE', // German
'it-IT', // Italian
'pt-BR', // Portuguese
'hi-IN', // Hindi
'zh-CN', // Chinese
'ja-JP', // Japanese
'ko-KR', // Korean
'ar-SA' // Arabic
];🎯 Voice Commands
The hook automatically recognizes voice commands for punctuation:
| Voice Command | Result | Example |
|---------------|--------|---------|
| "comma" | , | "Hello comma world" → "Hello, world" |
| "full stop" / "period" | . | "Hello full stop" → "Hello." |
| "question mark" | ? | "How are you question mark" → "How are you?" |
| "exclamation mark" | ! | "Wow exclamation mark" → "Wow!" |
| "colon" | : | "Note colon" → "Note:" |
| "semicolon" | ; | "First semicolon" → "First;" |
| "next line" / "new line" | \n | Line break |
Number Conversion:
- "three apples" → "3 apples"
- "one hundred" → "100"
- "one thousand" → "1000"
💡 Usage Examples
Example 1: Basic Text Input
function VoiceTextInput() {
const [text, setText] = useState('');
const { isListening, toggleListening } = useVoiceInput({
onTranscript: (transcript) => setText(transcript),
language: 'en-US'
});
return (
<div>
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
rows={10}
/>
<button onClick={toggleListening}>
{isListening ? '🛑 Stop' : '🎤 Start'}
</button>
</div>
);
}Example 2: Append to Existing Text
function AppendVoice() {
const [content, setContent] = useState('Existing content here. ');
const { isListening, toggleListening, setBaseText } = useVoiceInput({
onTranscript: (transcript) => setContent(transcript)
});
const handleToggle = () => {
if (!isListening) {
// Preserve existing content
setBaseText(content);
}
toggleListening();
};
return (
<div>
<textarea value={content} onChange={(e) => setContent(e.target.value)} />
<button onClick={handleToggle}>
{isListening ? 'Stop' : 'Append Voice'}
</button>
</div>
);
}Example 3: Multi-language Support
function MultiLanguageVoice() {
const [text, setText] = useState('');
const [language, setLanguage] = useState('en-US');
const { isListening, toggleListening } = useVoiceInput({
onTranscript: (transcript) => setText(transcript),
language: language
});
return (
<div>
<select value={language} onChange={(e) => setLanguage(e.target.value)}>
<option value="en-US">English (US)</option>
<option value="es-ES">Spanish</option>
<option value="hi-IN">Hindi</option>
<option value="zh-CN">Chinese</option>
</select>
<textarea value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={toggleListening}>
{isListening ? 'Stop' : 'Start'}
</button>
</div>
);
}Example 4: With Error Handling
function RobustVoiceInput() {
const [text, setText] = useState('');
const {
isListening,
isSupported,
error,
toggleListening
} = useVoiceInput({
onTranscript: (transcript) => setText(transcript)
});
if (!isSupported) {
return (
<div className="error">
⚠️ Voice input not supported. Please use Chrome, Edge, or Safari.
</div>
);
}
return (
<div>
{error && (
<div className="error">
Error: {error}
{error.includes('denied') && (
<div>Please allow microphone access in browser settings.</div>
)}
</div>
)}
<textarea value={text} onChange={(e) => setText(e.target.value)} />
<button onClick={toggleListening}>
{isListening ? 'Stop' : 'Start'}
</button>
</div>
);
}Example 5: ContentEditable Integration
function EditableVoiceInput() {
const [content, setContent] = useState('');
const contentRef = useRef(null);
const { isListening, toggleListening, setBaseText } = useVoiceInput({
onTranscript: (text) => {
if (contentRef.current) {
contentRef.current.innerHTML = text.replace(/\n/g, '<br>');
}
setContent(text);
}
});
const handleToggle = () => {
if (!isListening) {
setBaseText(contentRef.current?.textContent || '');
}
toggleListening();
};
return (
<div>
<div
ref={contentRef}
contentEditable
suppressContentEditableWarning
style={{
border: isListening ? '2px solid red' : '1px solid gray',
padding: '10px',
minHeight: '100px'
}}
>
{content}
</div>
<button onClick={handleToggle}>
{isListening ? 'Stop' : 'Start'}
</button>
</div>
);
}🎨 Real-World Example
Here's a complete example with all features:
import React, { useState, useCallback, useRef } from 'react';
import { useVoiceInput } from 'react-voice-to-text-converter';
export default function VoiceEditor() {
const [content, setContent] = useState('');
const contentRef = useRef(null);
const handleVoiceTranscript = useCallback((text, isFinal) => {
// Update DOM for real-time feel
if (contentRef.current) {
contentRef.current.textContent = text;
}
setContent(text);
// Auto-save on final
if (isFinal) {
console.log('Saved:', text);
}
}, []);
const {
isListening,
isSupported,
error,
toggleListening,
setBaseText
} = useVoiceInput({
onTranscript: handleVoiceTranscript,
language: 'en-US'
});
const handleToggleVoice = () => {
if (!isListening) {
setBaseText(contentRef.current?.textContent || '');
}
toggleListening();
};
if (!isSupported) {
return <div>Voice input not supported. Use Chrome, Edge, or Safari.</div>;
}
return (
<div style={{ padding: '20px', maxWidth: '800px', margin: '0 auto' }}>
<h2>Voice-Enabled Text Editor</h2>
{error && (
<div style={{
padding: '10px',
backgroundColor: '#f8d7da',
marginBottom: '10px'
}}>
Error: {error}
</div>
)}
<div
ref={contentRef}
contentEditable
suppressContentEditableWarning
onInput={(e) => {
if (!isListening) {
setContent(e.currentTarget.textContent);
}
}}
style={{
border: `2px solid ${isListening ? '#dc3545' : '#ced4da'}`,
padding: '15px',
minHeight: '200px',
marginBottom: '10px',
backgroundColor: isListening ? '#fff5f5' : 'white'
}}
>
{content}
</div>
<button
onClick={handleToggleVoice}
style={{
padding: '10px 20px',
backgroundColor: isListening ? '#dc3545' : '#007bff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
{isListening ? '🛑 Stop Recording' : '🎤 Start Voice Input'}
</button>
{isListening && (
<span style={{ marginLeft: '10px', color: '#dc3545' }}>
🎤 Recording...
</span>
)}
</div>
);
}🌐 Browser Support
| Browser | Support | Notes | |---------|---------|-------| | Chrome | ✅ Full Support | Recommended | | Edge | ✅ Full Support | Chromium-based | | Safari | ✅ Full Support | iOS 14.5+ | | Firefox | ⚠️ Limited | Requires flag enabled | | Opera | ✅ Full Support | Chromium-based |
Note: Always check the isSupported property before rendering voice controls.
⚙️ Best Practices
1. Always Check Browser Support
const { isSupported } = useVoiceInput({ onTranscript: handleTranscript });
if (!isSupported) {
return <div>Please use Chrome, Edge, or Safari</div>;
}2. Use useCallback for onTranscript
// ✅ Good - Prevents unnecessary re-renders
const handleTranscript = useCallback((text, isFinal) => {
setText(text);
}, []);
// ❌ Bad - Creates new function every render
const handleTranscript = (text, isFinal) => {
setText(text);
};3. Set Base Text Before Recording
const handleStartRecording = () => {
if (!isListening) {
setBaseText(existingContent); // Set BEFORE toggle
}
toggleListening();
};4. Handle Errors Gracefully
{error && (
<div className="error">
{error}
{error.includes('denied') && (
<div>Please allow microphone access</div>
)}
</div>
)}5. Prevent Manual Input During Recording
<textarea
value={content}
onChange={(e) => {
if (!isListening) { // Only when not recording
setContent(e.target.value);
}
}}
/>🐛 Troubleshooting
"Microphone access denied"
Solution:
- Click the microphone icon in browser address bar
- Select "Always allow" for your site
- Reload the page
Voice input not working
Check:
- Browser compatibility (use Chrome/Edge/Safari)
- Microphone permissions
- HTTPS connection (required for Web Speech API)
- Check
errorstate for specific error messages
Base text not preserved
Solution:
Call setBaseText() BEFORE toggleListening():
// ✅ Correct
setBaseText(existingText);
toggleListening();
// ❌ Wrong
toggleListening();
setBaseText(existingText); // Too late!🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
MIT © Abhishek MS
📞 Support
For issues, questions, or contributions:
- GitHub Issues: Create an issue
- Email: [email protected]
🙏 Acknowledgments
Built with the Web Speech API and lots of ☕
Made with ❤️ by Abhishek MS
