React text field components with speech-to-text dictation powered by Dictate Button.
Provides a thin integration layer that avoids DOM scanning and mutation, making it fully compatible with React and any CSS framework.
- No DOM Scanning: Declarative React components instead of inject scripts
- No DOM Mutation: Integrates naturally with React's virtual DOM
- Framework Agnostic: Works with any CSS framework (Tailwind, Bootstrap, etc.)
- TypeScript First: Full type safety and IntelliSense support
- React 19 Ready: Built for modern React
- Controlled & Uncontrolled: Supports both patterns
- Customizable: Full control over styling and behavior
pnpm add @dictate-button/reactYou need to register your app on dictate-button.io to use the @dictate-button/react components in your app.
To do that, visit dash.dictate-button.io, create a free account and register your site. No API key configuration needed - the service works automatically once your site is registered and verified.
import { DictateInput, DictateTextarea, DictateButton } from '@dictate-button/react';
function MyForm() {
return (
<div>
<DictateInput
type="text"
placeholder="Start typing or click the mic..."
className="your-input-class"
/>
<DictateTextarea
rows={5}
placeholder="Or speak here..."
className="your-textarea-class"
/>
<DictateButton
onDictateEnd={(text) => console.log('Transcribed:', text)}
/>
</div>
);
}The dictate-button package includes inject scripts that scan the DOM using querySelectorAll and mutate it by wrapping elements.
This integration layer provides:
- ✅ Declarative React components
- ✅ No DOM scanning or mutation
- ✅ Works with any CSS framework or styling approach
- ✅ Full TypeScript support
- ✅ React refs, controlled components, and hooks
A text input with integrated speech-to-text button.
<DictateInput
type="text"
placeholder="Type or speak..."
buttonSize={30}
onDictateEnd={(text) => console.log(text)}
/>A textarea with integrated speech-to-text button.
<DictateTextarea
rows={5}
placeholder="Type or speak..."
buttonSize={30}
onDictateEnd={(text) => console.log(text)}
/>A standalone dictate button for custom event-based implementations.
<DictateButton
size={30}
onDictateStart={() => console.log('Started')}
onDictateText={(text) => console.log('Interim:', text)}
onDictateEnd={(text) => console.log('Final:', text)}
onDictateError={(error) => console.error(error)}
/>Use this when you want to handle dictation events yourself without automatic text field integration.
For building custom text field integrations, use the hook to get text insertion event handlers:
import { useRef } from 'react';
import { DictateButton, useDictateButtonEventHandlers } from '@dictate-button/react';
function CustomInput() {
const inputRef = useRef<HTMLInputElement>(null);
const handlers = useDictateButtonEventHandlers(inputRef);
return (
<div style={{ position: 'relative' }}>
<input ref={inputRef} style={{ paddingRight: '40px' }} />
<DictateButton
{...handlers}
style={{ position: 'absolute', right: '4px', top: '50%', transform: 'translateY(-50%)' }}
/>
</div>
);
}All standard HTML input/textarea props are supported, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
buttonSize |
number |
30 |
Size of the dictate button in pixels |
buttonClassName |
string |
- | CSS class for the button |
apiEndpoint |
string |
- | Custom API endpoint for transcription |
language |
string |
'en' |
Language code (e.g., 'en', 'es', 'fr') |
theme |
'light' | 'dark' |
- | Button theme |
onDictateStart |
() => void |
- | Called when dictation starts (overrides text insertion if provided) |
onDictateText |
(text: string) => void |
- | Called with interim results (overrides text insertion if provided) |
onDictateEnd |
(text: string) => void |
- | Called with final transcription (overrides text insertion if provided) |
onDictateError |
(error: Error | string) => void |
- | Called on errors (overrides text insertion if provided) |
| Prop | Type | Default | Description |
|---|---|---|---|
size |
number |
30 |
Size of the button in pixels |
className |
string |
- | CSS class for the button |
style |
React.CSSProperties |
- | Inline styles for the button |
apiEndpoint |
string |
- | Custom API endpoint for transcription |
language |
string |
'en' |
Language code (e.g., 'en', 'es', 'fr') |
theme |
'light' | 'dark' |
- | Button theme |
onDictateStart |
() => void |
- | Called when dictation starts |
onDictateText |
(text: string) => void |
- | Called with interim results |
onDictateEnd |
(text: string) => void |
- | Called with final transcription |
onDictateError |
(error: Error | string) => void |
- | Called on errors |
These components accept standard className props and work with any CSS framework. Here's an example using Tailwind CSS with the popular cn() utility for conditional classes:
import { DictateInput } from '@dictate-button/react';
import { cn } from '@/lib/utils'; // clsx + tailwind-merge utility
<DictateInput
className={cn(
"flex h-10 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",
"disabled:cursor-not-allowed disabled:opacity-50"
)}
/>The cn() utility is commonly used in Tailwind projects to merge class names. It's not specific to any component library.
See EXAMPLES.md for comprehensive usage examples including:
- Basic usage
- Tailwind CSS styling
- Controlled components
- Custom event handlers
- React Hook Form integration
- Advanced hook usage
All components and hooks are fully typed. Import types as needed:
import type {
DictateButtonComponentProps,
DictateInputProps,
DictateTextareaProps,
UseDictateButtonEventHandlersReturn,
} from '@dictate-button/react';# Build the library
pnpm build
# Run tests
pnpm test
# Type checking
pnpm typecheck
# Watch mode for development
pnpm devApache-2.0
Built on top of dictate-button by the Dictate Button team.