import { DictateInput } from '@dictate-button/react';
function MyForm() {
return (
<div>
<label htmlFor="name">Name:</label>
<DictateInput
id="name"
type="text"
placeholder="Enter your name..."
className="my-input-class"
/>
</div>
);
}import { DictateTextarea } from '@dictate-button/react';
function MyTextArea() {
return (
<div>
<label htmlFor="bio">Biography:</label>
<DictateTextarea
id="bio"
rows={5}
placeholder="Tell us about yourself..."
className="my-textarea-class"
/>
</div>
);
}These components accept standard className props and work with any CSS framework. Here's an example using Tailwind CSS with the popular cn() utility (a combination of clsx and tailwind-merge):
import { DictateInput, DictateTextarea } from '@dictate-button/react';
import { cn } from '@/lib/utils'; // clsx + tailwind-merge utility
function StyledForm() {
return (
<div className="space-y-4">
<DictateInput
type="text"
placeholder="Email"
className={cn(
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background",
"file:border-0 file:bg-transparent file:text-sm file:font-medium",
"placeholder:text-muted-foreground",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
"disabled:cursor-not-allowed disabled:opacity-50"
)}
/>
<DictateTextarea
placeholder="Message"
className={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background",
"placeholder:text-muted-foreground",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
"disabled:cursor-not-allowed disabled:opacity-50"
)}
/>
</div>
);
}Note: The cn() utility is commonly used in Tailwind projects to merge class names. It's not specific to any particular component library.
import { useState } from 'react';
import { DictateInput } from '@dictate-button/react';
function ControlledInput() {
const [value, setValue] = useState('');
return (
<DictateInput
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Controlled input"
/>
);
}import { DictateInput } from '@dictate-button/react';
function EventHandlingExample() {
return (
<DictateInput
placeholder="Start speaking..."
onDictateStart={() => console.log('Dictation started')}
onDictateText={(text) => console.log('Interim text:', text)}
onDictateEnd={(finalText) => console.log('Final text:', finalText)}
onDictateError={(error) => console.error('Dictation error:', error)}
/>
);
}import { DictateTextarea } from '@dictate-button/react';
function CustomConfig() {
return (
<DictateTextarea
buttonSize={40}
apiEndpoint="https://your-api.com/transcribe"
language="es"
theme="dark"
placeholder="Habla ahora..."
/>
);
}import { useForm } from 'react-hook-form';
import { DictateInput, DictateTextarea } from '@dictate-button/react';
interface FormData {
name: string;
message: string;
}
function ReactHookFormExample() {
const { register, handleSubmit } = useForm<FormData>();
const onSubmit = (data: FormData) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<DictateInput
{...register('name')}
placeholder="Your name"
/>
<DictateTextarea
{...register('message')}
placeholder="Your message"
/>
<button type="submit">Submit</button>
</form>
);
}The standalone button for custom event handling:
import { useState } from 'react';
import { DictateButton } from '@dictate-button/react';
function CustomDictation() {
const [transcription, setTranscription] = useState('');
const [isRecording, setIsRecording] = useState(false);
return (
<div>
<DictateButton
size={40}
onDictateStart={() => {
setIsRecording(true);
console.log('Recording started');
}}
onDictateText={(interimText) => {
console.log('Interim:', interimText);
setTranscription(interimText);
}}
onDictateEnd={(finalText) => {
setIsRecording(false);
console.log('Final:', finalText);
setTranscription(finalText);
}}
onDictateError={(error) => {
setIsRecording(false);
console.error('Error:', error);
}}
/>
{isRecording && <p>Recording...</p>}
{transcription && <p>Result: {transcription}</p>}
</div>
);
}import { useState } from 'react';
import { DictateButton } from '@dictate-button/react';
function TranscriptionLogger() {
const [logs, setLogs] = useState<string[]>([]);
const [currentText, setCurrentText] = useState('');
const addLog = (message: string) => {
setLogs((prev) => [...prev, `${new Date().toLocaleTimeString()}: ${message}`]);
};
return (
<div>
<DictateButton
size={50}
language="en"
theme="dark"
onDictateStart={() => {
addLog('Dictation started');
setCurrentText('');
}}
onDictateText={(text) => {
setCurrentText(text);
addLog(`Interim: "${text}"`);
}}
onDictateEnd={(text) => {
addLog(`Final: "${text}"`);
setCurrentText(text);
}}
onDictateError={(error) => {
addLog(`Error: ${error}`);
}}
/>
<div>
<h3>Current Transcription:</h3>
<p>{currentText || 'Click the button to start'}</p>
</div>
<div>
<h3>Event Log:</h3>
<ul>
{logs.map((log, i) => (
<li key={i}>{log}</li>
))}
</ul>
</div>
</div>
);
}import { useState } from 'react';
import { DictateButton } from '@dictate-button/react';
function MultiLanguageDictation() {
const [language, setLanguage] = useState<'en' | 'es' | 'fr'>('en');
const [results, setResults] = useState<Record<string, string>>({
en: '',
es: '',
fr: '',
});
return (
<div>
<div>
<label>
Language:
<select
value={language}
onChange={(e) => setLanguage(e.target.value as 'en' | 'es' | 'fr')}
>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
</select>
</label>
<DictateButton
language={language}
onDictateEnd={(text) => {
setResults((prev) => ({
...prev,
[language]: text,
}));
}}
/>
</div>
<div>
<h3>Results:</h3>
<p>English: {results.en}</p>
<p>Spanish: {results.es}</p>
<p>French: {results.fr}</p>
</div>
</div>
);
}import { useState } from 'react';
import { DictateButton } from '@dictate-button/react';
interface DictationEntry {
id: string;
text: string;
timestamp: Date;
}
function DictationHistory() {
const [history, setHistory] = useState<DictationEntry[]>([]);
const handleDictateEnd = (text: string) => {
const entry: DictationEntry = {
id: crypto.randomUUID(),
text,
timestamp: new Date(),
};
setHistory((prev) => [entry, ...prev]);
};
return (
<div>
<div>
<h2>Dictate Something</h2>
<DictateButton onDictateEnd={handleDictateEnd} />
</div>
<div>
<h2>History</h2>
{history.length === 0 ? (
<p>No dictations yet</p>
) : (
<ul>
{history.map((entry) => (
<li key={entry.id}>
<strong>{entry.timestamp.toLocaleString()}</strong>
<p>{entry.text}</p>
</li>
))}
</ul>
)}
</div>
</div>
);
}For complete control over text field integrations, you can use the useDictateButtonEventHandlers hook with the DictateButton component:
import { useRef } from 'react';
import { DictateButton, useDictateButtonEventHandlers } from '@dictate-button/react';
function CustomTextInput() {
const inputRef = useRef<HTMLInputElement>(null);
const handlers = useDictateButtonEventHandlers(inputRef);
return (
<div style={{ position: 'relative' }}>
<input
ref={inputRef}
type="text"
style={{ paddingRight: '40px' }}
placeholder="Custom text input with dictation"
/>
<DictateButton
{...handlers}
size={30}
style={{
position: 'absolute',
right: '8px',
top: '50%',
transform: 'translateY(-50%)',
}}
/>
</div>
);
}You can combine the text insertion handlers with your own custom logic:
import { useRef } from 'react';
import { DictateButton, useDictateButtonEventHandlers } from '@dictate-button/react';
function CustomTextInputWithLogging() {
const inputRef = useRef<HTMLInputElement>(null);
const textHandlers = useDictateButtonEventHandlers(inputRef);
const handleDictateStart = () => {
textHandlers.onDictateStart();
console.log('User started dictating');
};
const handleDictateEnd = (text: string) => {
textHandlers.onDictateEnd(text);
console.log('Transcription complete:', text);
// Send analytics, save to backend, etc.
};
return (
<div style={{ position: 'relative' }}>
<input
ref={inputRef}
type="text"
style={{ paddingRight: '40px' }}
/>
<DictateButton
onDictateStart={handleDictateStart}
onDictateText={textHandlers.onDictateText}
onDictateEnd={handleDictateEnd}
onDictateError={textHandlers.onDictateError}
size={30}
style={{
position: 'absolute',
right: '8px',
top: '50%',
transform: 'translateY(-50%)',
}}
/>
</div>
);
}All components and hooks are fully typed:
import type {
DictateButtonComponentProps,
DictateInputProps,
DictateTextareaProps,
UseDictateButtonEventHandlersReturn,
DictateButtonElement,
} from '@dictate-button/react';
const buttonProps: DictateButtonComponentProps = {
size: 30,
apiEndpoint: 'https://api.example.com',
language: 'en',
theme: 'light',
onDictateEnd: (text: string) => console.log(text),
};
const inputProps: DictateInputProps = {
buttonSize: 30,
apiEndpoint: 'https://api.example.com',
language: 'en',
theme: 'light',
onDictateEnd: (text: string) => console.log(text),
};Traditional inject approach (NOT used here):
// ❌ Scans and mutates DOM - not React friendly
import { injectDictateButton } from 'dictate-button/libs/injectDictateButton';
injectDictateButton('input, textarea'); // Scans all inputsThis React integration (✅ Recommended):
// ✅ Declarative, no DOM scanning, React-friendly
import { DictateInput } from '@dictate-button/react';
<DictateInput />The React integration:
- No DOM scanning with
querySelectorAll - No direct DOM mutation
- Fully declarative and React-friendly
- Works with any CSS framework or styling approach
- Supports refs, controlled components, and React patterns
- Full TypeScript support