
Syncfusion Angular Speech To Text
- 200 installs
- Updated August 4, 2026
- syncfusion/angular-ui-components-skills
Use syncfusion-angular-speech-to-text for development tasks
About
syncfusion-angular-speech-to-text: A skill for development. This provides functionality for development workflows.
- syncfusion-angular-speech-to-text
Syncfusion Angular Speech To Text by the numbers
- 200 all-time installs (skills.sh)
- +5 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,977 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/angular-ui-components-skills --skill syncfusion-angular-speech-to-textAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 200 |
|---|---|
| Last updated | August 4, 2026 |
| Repository | syncfusion/angular-ui-components-skills ↗ |
What it does
Use syncfusion-angular-speech-to-text for development tasks
Files
Syncfusion Angular SpeechToText Component
Component Overview
The Syncfusion Angular SpeechToText component is a lightweight, accessible control that converts spoken words into text using the browser's native Speech Recognition API.
Key Capabilities:
- Real-time Transcription - Convert speech to text with interim and final results
- Button Customization - Configure icon, text, positioning, and styling for start/stop buttons
- Tooltip Support - Add tooltips with custom content and positioning for better UX
- Events & Interactions - Handle listening state changes, errors, and transcript updates
- Methods - Programmatic control via startListening() and stopListening() methods
- Language Support - Multi-language recognition with locale configuration
- Globalization - RTL support and localization for international applications
- Styling Options - CSS classes, HTML attributes, and theme integration
- Security Features - HTTPS enforcement and explicit microphone permissions
- Browser Compatibility - Chrome 25+, Edge 79+, Safari 12+, Opera 30+
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Package installation (Ivy vs ngcc compatibility)
- Angular project setup and configuration
- CSS imports and theme management
- Module imports and basic component rendering
- Button content customization
- Complete working example with TextArea integration
Button Customization
📄 Read: references/button-customization.md
- ButtonSettingsModel configuration
- Start and stop button content labels
- Icon customization with CSS classes
- Icon positioning (top, bottom, left, right)
- Primary button styling for emphasis
- Visual style variations and best practices
Tooltip Configuration
📄 Read: references/tooltip-configuration.md
- TooltipSettingsModel setup
- Tooltip content for start and stop states
- Tooltip positioning relative to button
- Enabling and disabling tooltips
- Practical tooltip usage patterns
Styling and Appearance
📄 Read: references/styling-and-appearance.md
- CSS class system (e-primary, e-outline, e-info, e-success, e-warning, e-danger)
- Custom styling with cssClass property
- Theme configuration and integration
- HTML attributes customization
- Custom CSS examples and advanced styling
Events and Methods
📄 Read: references/events-and-methods.md
- Event types (created, onStart, onStop, onError, transcriptChanged)
- Event handler implementation and event arguments (including event, name properties)
- Programmatic control with startListening(), stopListening(), and destroy() methods
- Controlling component state with listeningState property (Inactive, Listening, Stopped)
- Canceling listening with the cancel property in onStart event
- Distinguishing user vs programmatic actions with isInteracted property
- Event handling patterns for different scenarios
- Managing listening lifecycle programmatically
- State transition management and workflow control
- Component cleanup and memory management
Speech Recognition Features
📄 Read: references/speech-recognition-features.md
- Retrieving transcript from speech input
- Setting recognition language (lang property)
- Interim results configuration (allowInterimResults)
- Listening state management (Inactive, Listening, Stopped)
- Tooltip visibility control (showTooltip)
- Disabled state configuration
- State persistence with enablePersistence property
- State monitoring and status management
- Custom state management with localStorage
Internationalization
📄 Read: references/internationalization.md
- Localization setup using L10n.load()
- Translation key mapping for error messages and labels
- Multi-language implementation examples
- RTL (Right-to-Left) support enablement
- Language-specific configuration best practices
Security and Error Handling
📄 Read: references/security-and-error-handling.md
- Security risks and mitigation strategies
- Data transmission and privacy concerns
- HTTPS enforcement and MITM prevention
- Microphone permission management
- Error type handling (no-speech, aborted, audio-capture, not-allowed, service-not-allowed, network, unsupported-browser, default)
- Browser support matrix and compatibility
Quick Start Example
Here's a minimal working SpeechToText component with transcript output:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Common Patterns
Pattern 1: Customized Button with Events
Implement a styled button with custom labels and event handlers:
public buttonSettings: ButtonSettingsModel = {
content: 'Start Listening',
stopContent: 'Stop Listening',
iconCss: 'e-icons e-play',
stopIconCss: 'e-icons e-pause',
isPrimary: true
};
onListeningStart(args: StartListeningEventArgs): void {
console.log('Listening started');
}
onListeningStop(args: StopListeningEventArgs): void {
console.log('Listening stopped');
}Pattern 2: Multi-Language Support
Configure localization for international applications:
L10n.load({
'de': {
"speech-to-text": {
"startAriaLabel": "Drücken Sie, um zu sprechen",
"stopAriaLabel": "Drücken Sie, um zu stoppen",
"noSpeechError": "Keine Sprache erkannt"
}
}
});Pattern 3: Programmatic Control
Manage listening state programmatically without user button clicks:
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
public startListening(): void {
this.speechToText.startListening();
}
public stopListening(): void {
this.speechToText.stopListening();
}Pattern 4: Error Handling
Handle speech recognition errors gracefully:
onErrorHandler(args: ErrorEventArgs): void {
if (args.error === 'no-speech') {
console.log('No speech detected. Please try again.');
} else if (args.error === 'not-allowed') {
console.log('Microphone permission denied.');
}
}Pattern 5: Conditional Listening with Cancel
Control when listening can start using the cancel property:
onListeningStart(args: StartListeningEventArgs): void {
// Cancel listening if conditions aren't met
if (!this.hasPermission || !navigator.onLine) {
args.cancel = true;
alert('Cannot start listening. Check permissions and connection.');
return;
}
// Track if user or system triggered the action
if (args.isInteracted) {
console.log('User clicked the button');
} else {
console.log('Started programmatically');
}
}Pattern 6: Distinguishing Interim vs Final Results
Handle interim and final transcripts differently:
onTranscriptChange(args: TranscriptChangedEventArgs): void {
if (args.isInterimResult) {
// Show interim results in real-time (lighter styling)
this.interimText = args.transcript;
} else {
// Process final results (save, send to API, etc.)
this.finalText = args.transcript;
this.saveTranscript(args.transcript);
}
}Pattern 7: Controlling Component State
Programmatically control the component's operational state:
import { SpeechToTextState } from '@syncfusion/ej2-angular-inputs';
export class AppComponent {
public currentState: SpeechToTextState = SpeechToTextState.Inactive;
// Set component to listening state
startListening(): void {
this.currentState = SpeechToTextState.Listening;
}
// Set component to stopped state
stopListening(): void {
this.currentState = SpeechToTextState.Stopped;
}
// Reset to inactive state
resetState(): void {
this.currentState = SpeechToTextState.Inactive;
}
}Pattern 8: Proper Component Cleanup
Ensure proper cleanup to prevent memory leaks:
import { OnDestroy } from '@angular/core';
export class AppComponent implements OnDestroy {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
ngOnDestroy(): void {
if (this.speechToText) {
this.speechToText.stopListening();
this.speechToText.destroy();
}
}
}Key Props
| Prop | Type | Default | Purpose |
|---|---|---|---|
buttonSettings | ButtonSettingsModel | - | Configure button appearance and labels |
tooltipSettings | TooltipSettingsModel | - | Customize tooltip content and position |
lang | string | 'en-US' | Set speech recognition language |
allowInterimResults | boolean | true | Enable real-time interim transcription |
transcript | string | '' | Current recognized text (read-only) |
listeningState | SpeechToTextState | Inactive | Monitor listening status (Inactive, Listening, Stopped) |
showTooltip | boolean | true | Display tooltip on hover |
disabled | boolean | false | Disable component interaction |
enablePersistence | boolean | false | Persist component state across page reloads |
enableRtl | boolean | false | Enable right-to-left text direction |
cssClass | string | - | Apply custom CSS classes |
htmlAttributes | object | - | Set custom HTML attributes on button |
locale | string | 'en-US' | Set UI localization language |
Browser Requirements
The SpeechToText component requires:
- Active internet connection for speech recognition processing
- Browser Speech Recognition API support:
- Chrome 25+
- Edge 79+
- Safari 12+
- Opera 30+
- Firefox: Not Supported
- Microphone hardware connected to the system
- User microphone permission granted in browser settings
- HTTPS context for secure microphone access (recommended)
Button Customization
Table of Contents
- ButtonSettingsModel Overview
- Start Content
- Stop Content
- Icon Customization
- Icon Positioning
- Primary Button Styling
- Complete Customization Example
ButtonSettingsModel Overview
The ButtonSettingsModel interface provides comprehensive control over the appearance and behavior of the SpeechToText button. It allows you to customize labels, icons, positioning, and styling.
Start Content
The content property defines the text displayed on the button when speech recognition is inactive (ready to listen):
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs, ButtonSettingsModel } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[buttonSettings]="buttonSettings">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public buttonSettings: ButtonSettingsModel = {
content: 'Speak Now' // Text displayed when ready to listen
};
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Stop Content
The stopContent property defines the text displayed while speech recognition is actively listening:
public buttonSettings: ButtonSettingsModel = {
content: 'Start Listening',
stopContent: 'Stop Listening' // Text displayed while listening
};This gives users clear visual feedback about what state the component is in.
Icon Customization
Start Icon (iconCss)
Apply a CSS class to the icon shown during inactive state:
public buttonSettings: ButtonSettingsModel = {
content: 'Start',
iconCss: 'e-icons e-play' // Play icon for start state
};Stop Icon (stopIconCss)
Apply a CSS class to the icon shown during active listening:
public buttonSettings: ButtonSettingsModel = {
content: 'Start',
stopContent: 'Stop',
iconCss: 'e-icons e-play',
stopIconCss: 'e-icons e-pause' // Pause icon for stop state
};Common Icon Classes:
e-play- Play icone-pause- Pause icone-mic- Microphone icone-search- Search icone-close- Close/X icon
Icon Positioning
The iconPosition property controls where the icon appears relative to the text:
public buttonSettings: ButtonSettingsModel = {
content: 'Listen',
iconCss: 'e-icons e-play',
iconPosition: 'Right' // Icon on the right side of text
};Available Positions:
'Left'- Icon appears left of the text'Right'- Icon appears right of the text'Top'- Icon appears above the text'Bottom'- Icon appears below the text
Example with Different Positions:
// Icon on the left
public leftIconSettings: ButtonSettingsModel = {
content: 'Listen',
iconCss: 'e-icons e-play',
iconPosition: 'Left'
};
// Icon on top
public topIconSettings: ButtonSettingsModel = {
content: 'Speak',
iconCss: 'e-icons e-play',
iconPosition: 'Top'
};Primary Button Styling
The isPrimary property applies primary styling to the button, making it more prominent:
public buttonSettings: ButtonSettingsModel = {
content: 'Start Listening',
stopContent: 'Stop Listening',
isPrimary: true // Apply primary styling
};What isPrimary Does:
- Applies the primary color from the theme
- Increases visual prominence
- Alternative to manually adding
e-primaryCSS class - Provides consistent theming
Complete Customization Example
Here's a comprehensive example combining all button customization options:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs, ButtonSettingsModel } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Customized SpeechToText Button</h3>
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[buttonSettings]="customButtonSettings">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public customButtonSettings: ButtonSettingsModel = {
content: 'Click to Speak', // Text when ready
stopContent: 'Stop Recording', // Text while listening
iconCss: 'e-icons e-play', // Start icon
stopIconCss: 'e-icons e-pause', // Stop icon
iconPosition: 'Left', // Icon on left side
isPrimary: true // Apply primary styling
};
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}CSS for Styling:
.speechText-container {
margin: 50px auto;
gap: 20px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 600px;
}
.speechText-container h3 {
color: #333;
margin-bottom: 20px;
}
/* Custom button styling if needed */
.speechText-container .e-speech-to-text {
padding: 10px 20px;
border-radius: 4px;
}Best Practices
1. Keep Labels Short: Use concise text like "Listen" or "Speak" for better UX 2. Use Contrasting Icons: Choose icons that clearly indicate the action 3. Primary Styling: Use isPrimary: true for call-to-action buttons 4. Consistent Positioning: Keep icon positions consistent across your application 5. Test Accessibility: Ensure labels are clear for screen readers 6. Consider RTL: If supporting RTL languages, test icon positioning
Edge Cases
Empty Content: If you don't specify content, the button will show a default microphone icon.
Same Icon for Both States: If both states need the same icon, provide only iconCss:
public buttonSettings: ButtonSettingsModel = {
content: 'Listen',
stopContent: 'Listening...',
iconCss: 'e-icons e-mic' // Same icon for both states
};Dynamic Content Change: You can update button settings after initialization:
updateButtonSettings(): void {
this.customButtonSettings.content = 'New Label';
}Events and Methods
Table of Contents
- Events Overview
- Event Types
- Event Handling
- Methods
- Programmatic Control Example
- Canceling Listening with StartListeningEventArgs
- Distinguishing User vs Programmatic Actions
- Distinguishing Interim vs Final Results
- Controlling Component State with listeningState
Events Overview
The SpeechToText component emits five events during the speech recognition lifecycle. These events allow you to respond to user interactions and state changes.
| Event | Args | Description |
|---|---|---|
created | - | Triggers when the component is fully rendered |
onStart | StartListeningEventArgs | Triggers when speech recognition begins |
onStop | StopListeningEventArgs | Triggers when speech recognition stops |
onError | ErrorEventArgs | Triggers when an error occurs |
transcriptChanged | TranscriptChangedEventArgs | Triggers when transcription updates |
Event Types
created Event
Fires when the SpeechToText component completes rendering:
onCreated(): void {
console.log('SpeechToText component is ready to use');
}onStart Event
Fires when the user clicks the button and speech recognition begins. Provides StartListeningEventArgs:
onListeningStart(args: StartListeningEventArgs): void {
console.log('Listening started');
console.log('Listening state:', args.listeningState);
console.log('User interaction:', args.isInteracted);
console.log('Event name:', args.name);
// Cancel listening based on a condition
if (someCondition) {
args.cancel = true; // Prevents listening from starting
}
}StartListeningEventArgs Properties:
listeningState(SpeechToTextState): Current component stateisInteracted(boolean):trueif triggered by user click,falseif programmaticcancel(boolean): Set totrueto prevent listening from startingevent(Event): The browser's native event objectname(string): The event name ('onStart')
onStop Event
Fires when speech recognition stops. Provides StopListeningEventArgs:
onListeningStop(args: StopListeningEventArgs): void {
console.log('Listening stopped');
console.log('Listening state:', args.listeningState);
console.log('User interaction:', args.isInteracted);
console.log('Event name:', args.name);
}StopListeningEventArgs Properties:
listeningState(SpeechToTextState): Current component stateisInteracted(boolean):trueif triggered by user click,falseif programmaticevent(Event): The browser's native event objectname(string): The event name ('onStop')
onError Event
Fires when an error occurs during speech recognition. Provides ErrorEventArgs:
onErrorHandler(args: ErrorEventArgs): void {
console.log('Error occurred:', args.error);
console.log('Error message:', args.errorMessage);
console.log('Event name:', args.name);
console.log('Native event:', args.event);
// Access browser's native error event for advanced debugging
if (args.event) {
console.log('Event timestamp:', args.event.timeStamp);
console.log('Event type:', args.event.type);
}
}ErrorEventArgs Properties:
error(string): Error type code (e.g., 'no-speech', 'not-allowed')errorMessage(string): Human-readable error descriptionevent(Event): The browser's native error event objectname(string): The event name ('onError')
transcriptChanged Event
Fires each time the transcript updates (continuously during listening). Provides TranscriptChangedEventArgs:
onTranscriptChange(args: TranscriptChangedEventArgs): void {
console.log('Transcript:', args.transcript);
console.log('Is interim result:', args.isInterimResult);
if (args.isInterimResult) {
console.log('Interim (partial) result:', args.transcript);
} else {
console.log('Final result:', args.transcript);
}
}TranscriptChangedEventArgs Properties:
transcript(string): The recognized textisInterimResult(boolean):trueif result is partial/interim,falseif finalevent(Event): The browser's native speech recognition eventname(string): The event name ('transcriptChanged')
Event Handling
Basic Event Handler Setup
import { Component } from '@angular/core';
import { SpeechToTextModule, TranscriptChangedEventArgs, ErrorEventArgs, StartListeningEventArgs, StopListeningEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext
(created)="onCreated()"
(transcriptChanged)="onTranscriptChange($event)"
(onStart)="onListeningStart($event)"
(onStop)="onListeningStop($event)"
(onError)="onErrorHandler($event)">
</button>
</div>`
})
export class AppComponent {
onTranscriptChange(args: TranscriptChangedEventArgs): void {
console.log('Transcript:', args.transcript);
}
onListeningStart(args: StartListeningEventArgs): void {
console.log('Listening started');
}
onListeningStop(args: StopListeningEventArgs): void {
console.log('Listening stopped');
}
onErrorHandler(args: ErrorEventArgs): void {
console.log('Error:', args.error);
}
onCreated(): void {
console.log('Component created');
}
}Event Handlers with State Management
import { Component } from '@angular/core';
import { SpeechToTextModule, TranscriptChangedEventArgs, ErrorEventArgs, StartListeningEventArgs, StopListeningEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<div class="status">
<p>Status: <strong>{{ status }}</strong></p>
<p>Last Transcript: {{ lastTranscript }}</p>
</div>
<button ejs-speechtotext
(created)="onCreated()"
(transcriptChanged)="onTranscriptChange($event)"
(onStart)="onListeningStart($event)"
(onStop)="onListeningStop($event)"
(onError)="onErrorHandler($event)">
</button>
</div>`
})
export class AppComponent {
public status: string = 'Idle';
public lastTranscript: string = '';
onCreated(): void {
this.status = 'Ready';
}
onListeningStart(args: StartListeningEventArgs): void {
this.status = 'Listening...';
}
onListeningStop(args: StopListeningEventArgs): void {
this.status = 'Stopped';
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.lastTranscript = args.transcript;
}
onErrorHandler(args: ErrorEventArgs): void {
this.status = `Error: ${args.error}`;
}
}Methods
startListening() Method
Programmatically initiates speech recognition:
public startListening(): void {
this.speechToText.startListening();
}stopListening() Method
Programmatically stops speech recognition:
public stopListening(): void {
this.speechToText.stopListening();
}destroy() Method
Destroys the SpeechToText component instance and cleans up resources:
import { Component, ViewChild, OnDestroy } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button #speechtotext ejs-speechtotext></button>
</div>`
})
export class AppComponent implements OnDestroy {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
ngOnDestroy(): void {
// Clean up the component before destroying
if (this.speechToText) {
this.speechToText.destroy();
}
}
}When to Use destroy():
1. Component Cleanup: Call in ngOnDestroy() lifecycle hook 2. Dynamic Components: When removing components dynamically 3. Memory Management: To prevent memory leaks in single-page applications 4. Route Changes: Clean up before navigating away 5. Conditional Rendering: When toggling component visibility
Complete Cleanup Example:
import { Component, ViewChild, OnDestroy } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container" *ngIf="isComponentVisible">
<h3>SpeechToText with Proper Cleanup</h3>
<button #speechtotext
ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)">
</button>
<ejs-textarea #outputTextarea
[(value)]="transcript"
rows="5"
cols="50">
</ejs-textarea>
<button class="e-btn e-danger" (click)="destroyComponent()">
Destroy Component
</button>
</div>
<div *ngIf="!isComponentVisible">
<p>Component has been destroyed.</p>
<button class="e-btn e-primary" (click)="recreateComponent()">
Recreate Component
</button>
</div>`
})
export class AppComponent implements OnDestroy {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public isComponentVisible: boolean = true;
public transcript: string = '';
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.transcript = args.transcript;
}
destroyComponent(): void {
// Stop listening if active
if (this.speechToText) {
try {
this.speechToText.stopListening();
} catch (e) {
console.log('Already stopped or not listening');
}
// Destroy the component
this.speechToText.destroy();
console.log('Component destroyed successfully');
}
// Hide the component
this.isComponentVisible = false;
}
recreateComponent(): void {
this.isComponentVisible = true;
console.log('Component recreated');
}
ngOnDestroy(): void {
// Always clean up when the parent component is destroyed
if (this.speechToText) {
try {
this.speechToText.stopListening();
this.speechToText.destroy();
console.log('Cleanup completed in ngOnDestroy');
} catch (error) {
console.error('Error during cleanup:', error);
}
}
}
}Best Practices for destroy():
1. Always Call in ngOnDestroy: Ensure cleanup when component is removed 2. Stop Listening First: Call stopListening() before destroy() 3. Error Handling: Wrap in try-catch to handle edge cases 4. Check Instance: Verify component exists before calling destroy 5. Clean Related Resources: Clear any timers, subscriptions, or event listeners 6. Avoid Memory Leaks: Essential in SPAs with frequent route changes
Programmatic Control Example
Here's a complete example with programmatic control of speech recognition:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs, StartListeningEventArgs, StopListeningEventArgs, ErrorEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Programmatic Speech Control</h3>
<div class="status-panel">
<p>Status: <strong [ngClass]="statusClass">{{ status }}</strong></p>
<p>Transcript: {{ currentTranscript }}</p>
<p>Error: {{ lastError }}</p>
</div>
<div class="button-group">
<button class="e-btn e-primary" (click)="startListening()">
Start Listening
</button>
<button class="e-btn e-danger" (click)="stopListening()">
Stop Listening
</button>
<button class="e-btn" (click)="clearTranscript()">
Clear
</button>
</div>
<button #speechtotext
ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
(onStart)="onListeningStart($event)"
(onStop)="onListeningStop($event)"
(onError)="onErrorHandler($event)"
style="display: none;">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
[(value)]="currentTranscript"
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public status: string = 'Idle';
public statusClass: string = 'status-idle';
public currentTranscript: string = '';
public lastError: string = '';
onCreated(): void {
this.status = 'Ready';
this.statusClass = 'status-ready';
}
onListeningStart(args: StartListeningEventArgs): void {
this.status = 'Listening...';
this.statusClass = 'status-listening';
this.lastError = '';
}
onListeningStop(args: StopListeningEventArgs): void {
this.status = 'Stopped';
this.statusClass = 'status-stopped';
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.currentTranscript = args.transcript;
}
onErrorHandler(args: ErrorEventArgs): void {
this.status = 'Error';
this.statusClass = 'status-error';
this.lastError = `${args.error}: ${args.errorMessage}`;
}
public startListening(): void {
this.speechToText.startListening();
}
public stopListening(): void {
this.speechToText.stopListening();
}
public clearTranscript(): void {
this.currentTranscript = '';
this.lastError = '';
}
}CSS Styling:
.speechText-container {
margin: 50px auto;
gap: 20px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 700px;
padding: 30px;
}
.status-panel {
padding: 20px;
background-color: #f9f9f9;
border-radius: 6px;
border-left: 4px solid #ccc;
width: 100%;
box-sizing: border-box;
}
.status-panel p {
margin: 8px 0;
font-size: 14px;
}
.status-panel strong {
font-size: 16px;
}
.status-idle { color: #666; }
.status-ready { color: #28a745; }
.status-listening { color: #007bff; font-weight: bold; }
.status-stopped { color: #ffc107; }
.status-error { color: #dc3545; }
.button-group {
display: flex;
gap: 10px;
width: 100%;
justify-content: center;
}
.button-group button {
padding: 10px 20px;
font-size: 14px;
border-radius: 4px;
}Canceling Listening with StartListeningEventArgs
Use the cancel property to prevent listening from starting based on conditions:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaComponent, TextAreaModule, StartListeningEventArgs, StopListeningEventArgs, TranscriptChangedEventArgs, ErrorEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Conditional Listening Control</h3>
<div class="permission-panel">
<label>
<input type="checkbox" [(ngModel)]="hasPermission">
Grant microphone permission
</label>
<p class="info">Listening will be canceled if permission is not granted.</p>
</div>
<div class="status-panel">
<p>Status: <strong>{{ statusMessage }}</strong></p>
</div>
<button #speechtotext
ejs-speechtotext
(onStart)="onListeningStart($event)"
(onStop)="onListeningStop($event)"
(transcriptChanged)="onTranscriptChange($event)"
(onError)="onErrorHandler($event)">
</button>
<ejs-textarea #outputTextarea
[(value)]="transcript"
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public hasPermission: boolean = false;
public statusMessage: string = 'Ready';
public transcript: string = '';
onListeningStart(args: StartListeningEventArgs): void {
// Check permission before allowing listening to start
if (!this.hasPermission) {
args.cancel = true; // Cancel the listening action
this.statusMessage = 'Listening canceled - Permission required';
alert('Please grant microphone permission before listening.');
return;
}
// Check if triggered by user or programmatically
if (args.isInteracted) {
this.statusMessage = 'User clicked - Listening started';
} else {
this.statusMessage = 'Programmatically triggered - Listening started';
}
console.log('Event name:', args.name);
console.log('Listening state:', args.listeningState);
}
onListeningStop(args: StopListeningEventArgs): void {
this.statusMessage = 'Listening stopped';
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.transcript = args.transcript;
}
onErrorHandler(args: ErrorEventArgs): void {
this.statusMessage = `Error: ${args.error}`;
}
}Common Use Cases for cancel:
1. Permission Checks: Cancel if microphone permission is not granted 2. Validation: Cancel if required fields are not filled 3. Rate Limiting: Cancel if user has exceeded usage limits 4. Business Logic: Cancel based on application state or user role 5. Network Check: Cancel if offline and speech service requires internet
Advanced Example with Multiple Conditions:
onListeningStart(args: StartListeningEventArgs): void {
// Multiple validation checks
const validationResult = this.validateListeningConditions();
if (!validationResult.isValid) {
args.cancel = true;
this.showValidationError(validationResult.message);
return;
}
// Log interaction type for analytics
if (args.isInteracted) {
this.logUserAction('speech_listening_started_manual');
} else {
this.logUserAction('speech_listening_started_auto');
}
this.statusMessage = 'Listening...';
}
private validateListeningConditions(): { isValid: boolean; message: string } {
// Check microphone permission
if (!this.hasMicrophoneAccess()) {
return { isValid: false, message: 'Microphone access required' };
}
// Check network connectivity
if (!navigator.onLine) {
return { isValid: false, message: 'Internet connection required' };
}
// Check if user is authenticated
if (!this.isUserAuthenticated()) {
return { isValid: false, message: 'Please login to use speech recognition' };
}
// Check rate limits
if (this.hasExceededRateLimit()) {
return { isValid: false, message: 'Usage limit exceeded. Please try later.' };
}
return { isValid: true, message: '' };
}
private showValidationError(message: string): void {
this.statusMessage = message;
// Show toast notification or modal
alert(message);
}Distinguishing User vs Programmatic Actions
Use the isInteracted property to determine how listening was triggered:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaModule, StartListeningEventArgs, StopListeningEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>User Interaction Tracking</h3>
<div class="stats-panel">
<p>Manual starts: <strong>{{ manualStarts }}</strong></p>
<p>Auto starts: <strong>{{ autoStarts }}</strong></p>
</div>
<button #speechtotext
ejs-speechtotext
(onStart)="onListeningStart($event)"
(onStop)="onListeningStop($event)">
</button>
<div class="control-buttons">
<button class="e-btn e-primary" (click)="startProgrammatically()">
Start Programmatically
</button>
</div>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
public manualStarts: number = 0;
public autoStarts: number = 0;
onListeningStart(args: StartListeningEventArgs): void {
if (args.isInteracted) {
// User clicked the button
this.manualStarts++;
console.log('User initiated listening');
this.trackAnalytics('speech_button_clicked');
} else {
// Started programmatically via API
this.autoStarts++;
console.log('Programmatically initiated listening');
this.trackAnalytics('speech_auto_started');
}
console.log('Event details:', {
name: args.name,
state: args.listeningState,
isUserAction: args.isInteracted
});
}
onListeningStop(args: StopListeningEventArgs): void {
if (args.isInteracted) {
console.log('User stopped listening');
this.trackAnalytics('speech_button_stopped');
} else {
console.log('Programmatically stopped listening');
this.trackAnalytics('speech_auto_stopped');
}
}
startProgrammatically(): void {
// This will trigger onStart with isInteracted = false
this.speechToText.startListening();
}
private trackAnalytics(eventName: string): void {
// Send to analytics service
console.log('Analytics:', eventName, new Date().toISOString());
}
}Use Cases for isInteracted:
1. Analytics Tracking: Differentiate user actions from automated actions 2. Conditional Logic: Apply different business rules based on trigger source 3. UI Updates: Show different messages for manual vs automatic starts 4. Rate Limiting: Apply different limits for user vs system actions 5. Logging: Track user behavior patterns vs system-initiated actions
Distinguishing Interim vs Final Results
Use the isInterimResult property to handle interim and final transcripts differently:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Interim vs Final Results</h3>
<div class="result-display">
<div class="interim-result">
<h4>Interim (Live):</h4>
<p>{{ interimTranscript || 'Waiting for speech...' }}</p>
</div>
<div class="final-result">
<h4>Final:</h4>
<p>{{ finalTranscript || 'No final result yet' }}</p>
</div>
</div>
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[allowInterimResults]="true">
</button>
<ejs-textarea #outputTextarea
[(value)]="fullTranscript"
rows="5"
cols="50"
resizeMode="None"
placeholder="All transcripts...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public interimTranscript: string = '';
public finalTranscript: string = '';
public fullTranscript: string = '';
onTranscriptChange(args: TranscriptChangedEventArgs): void {
if (args.isInterimResult) {
// Handle interim (partial) results - updates in real-time
this.interimTranscript = args.transcript;
console.log('Interim:', args.transcript);
} else {
// Handle final results - stable recognized text
this.finalTranscript = args.transcript;
this.fullTranscript += args.transcript + ' ';
this.interimTranscript = ''; // Clear interim when final arrives
console.log('Final:', args.transcript);
}
}
}Use Cases for isInterimResult:
1. Live Display: Show interim results for real-time feedback 2. Auto-save: Save only final results to avoid saving incomplete text 3. Analytics: Track how long users take to finalize speech 4. UI Indicators: Show different styling for interim vs final text
CSS for Visual Distinction:
.result-display {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.interim-result, .final-result {
flex: 1;
padding: 15px;
border-radius: 6px;
min-height: 80px;
}
.interim-result {
background-color: #fff3cd;
border: 2px dashed #ffc107;
}
.interim-result h4 {
color: #856404;
}
.final-result {
background-color: #d1e7dd;
border: 2px solid #28a745;
}
.final-result h4 {
color: #0f5132;
}Controlling Component State with listeningState
The listeningState property represents the current operational state of the component and can be used to both monitor and control the component's behavior. This property helps manage transitions between different states.
Property Details:
- Type:
SpeechToTextState - Default:
'Inactive' - Possible Values:
'Inactive': Component is idle and ready to start listening'Listening': Component is actively listening for speech input'Stopped': Listening has been stopped
Reading the Current State
Monitor the component's state through event arguments:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, StartListeningEventArgs, StopListeningEventArgs, SpeechToTextState } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Monitoring Component State</h3>
<div class="state-indicator">
<p>Current State: <strong [ngClass]="getStateClass()">{{ currentState }}</strong></p>
</div>
<button #speechtotext
ejs-speechtotext
(onStart)="onListeningStart($event)"
(onStop)="onListeningStop($event)">
</button>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
public currentState: string = 'Inactive';
onListeningStart(args: StartListeningEventArgs): void {
this.currentState = args.listeningState;
console.log('State changed to:', args.listeningState);
}
onListeningStop(args: StopListeningEventArgs): void {
this.currentState = args.listeningState;
console.log('State changed to:', args.listeningState);
}
getStateClass(): string {
switch (this.currentState) {
case 'Listening': return 'state-active';
case 'Stopped': return 'state-stopped';
default: return 'state-inactive';
}
}
}Setting the Component State Programmatically
Control the component's state by setting the listeningState property:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaModule, TranscriptChangedEventArgs, SpeechToTextState } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Programmatic State Control</h3>
<div class="state-controls">
<button class="e-btn e-success" (click)="setStateToListening()">
Set to Listening
</button>
<button class="e-btn e-warning" (click)="setStateToStopped()">
Set to Stopped
</button>
<button class="e-btn e-info" (click)="setStateToInactive()">
Set to Inactive
</button>
</div>
<div class="state-display">
<p>Current State: <strong>{{ currentState }}</strong></p>
</div>
<button #speechtotext
ejs-speechtotext
[listeningState]="currentState"
(transcriptChanged)="onTranscriptChange($event)">
</button>
<ejs-textarea [(value)]="transcript"
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
public currentState: SpeechToTextState = SpeechToTextState.Inactive;
public transcript: string = '';
setStateToListening(): void {
this.currentState = SpeechToTextState.Listening;
console.log('State set to: Listening');
}
setStateToStopped(): void {
this.currentState = SpeechToTextState.Stopped;
console.log('State set to: Stopped');
}
setStateToInactive(): void {
this.currentState = SpeechToTextState.Inactive;
console.log('State set to: Inactive');
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.transcript = args.transcript;
}
}Managing State Transitions
Use state management to control workflow and user interactions:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaModule, TranscriptChangedEventArgs, SpeechToTextState, StartListeningEventArgs, StopListeningEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>State Transition Management</h3>
<div class="workflow-controls">
<button class="e-btn e-primary"
(click)="startWorkflow()"
[disabled]="!canStartWorkflow()">
Start Speech Workflow
</button>
<button class="e-btn e-danger"
(click)="stopWorkflow()"
[disabled]="!canStopWorkflow()">
Stop Workflow
</button>
<button class="e-btn" (click)="resetWorkflow()">
Reset
</button>
</div>
<div class="workflow-status">
<p>Workflow State: <strong [ngClass]="getWorkflowClass()">{{ workflowState }}</strong></p>
<p>Listening State: <strong>{{ listeningState }}</strong></p>
<p>Steps Completed: <strong>{{ stepsCompleted }}</strong></p>
</div>
<button #speechtotext
ejs-speechtotext
[listeningState]="listeningState"
(onStart)="onListeningStart($event)"
(onStop)="onListeningStop($event)"
(transcriptChanged)="onTranscriptChange($event)">
</button>
<ejs-textarea [(value)]="transcript"
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
public listeningState: SpeechToTextState = SpeechToTextState.Inactive;
public workflowState: string = 'Not Started';
public stepsCompleted: number = 0;
public transcript: string = '';
startWorkflow(): void {
// Set component to listening state
this.listeningState = SpeechToTextState.Listening;
this.workflowState = 'Active';
this.speechToText.startListening();
console.log('Workflow started - State:', this.listeningState);
}
stopWorkflow(): void {
// Set component to stopped state
this.listeningState = SpeechToTextState.Stopped;
this.workflowState = 'Stopped';
this.speechToText.stopListening();
console.log('Workflow stopped - State:', this.listeningState);
}
resetWorkflow(): void {
// Reset to inactive state
this.listeningState = SpeechToTextState.Inactive;
this.workflowState = 'Not Started';
this.stepsCompleted = 0;
this.transcript = '';
console.log('Workflow reset - State:', this.listeningState);
}
canStartWorkflow(): boolean {
return this.listeningState === SpeechToTextState.Inactive;
}
canStopWorkflow(): boolean {
return this.listeningState === SpeechToTextState.Listening;
}
onListeningStart(args: StartListeningEventArgs): void {
this.listeningState = args.listeningState;
console.log('Component state changed:', args.listeningState);
}
onListeningStop(args: StopListeningEventArgs): void {
this.listeningState = args.listeningState;
this.stepsCompleted++;
console.log('Component state changed:', args.listeningState);
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.transcript = args.transcript;
}
getWorkflowClass(): string {
switch (this.workflowState) {
case 'Active': return 'workflow-active';
case 'Stopped': return 'workflow-stopped';
default: return 'workflow-idle';
}
}
}CSS for State Visualization:
.state-indicator, .state-display, .workflow-status {
padding: 15px;
background-color: #f8f9fa;
border-radius: 6px;
margin-bottom: 15px;
}
.state-active, .workflow-active {
color: #28a745;
font-weight: bold;
}
.state-stopped, .workflow-stopped {
color: #ffc107;
font-weight: bold;
}
.state-inactive, .workflow-idle {
color: #6c757d;
}
.state-controls, .workflow-controls {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}Use Cases for State Control:
1. Workflow Management: Control multi-step processes with speech input 2. Conditional Logic: Enable/disable features based on listening state 3. UI Synchronization: Keep UI in sync with component state 4. State Persistence: Save and restore listening state 5. Automated Testing: Set specific states for testing scenarios 6. User Onboarding: Guide users through state transitions
State Transition Best Practices:
1. Always Check Current State: Verify state before transitions 2. Use Enum Values: Use SpeechToTextState enum for type safety 3. Handle Events: Listen to onStart/onStop to track state changes 4. Validate Transitions: Ensure valid state transitions (Inactive → Listening → Stopped) 5. Update UI: Provide visual feedback for state changes 6. Error Handling: Handle invalid state transitions gracefully
Best Practices
1. Always Handle Errors: Implement error handlers to gracefully handle failures 2. Provide User Feedback: Update UI to show listening status and transcript changes 3. Validate Transcript: Check if transcript is empty before processing 4. Use isInterimResult: Distinguish between interim and final results for better UX 5. Control Component State: Use listeningState to manage component behavior programmatically 6. Debounce Updates: For frequent interim updates, consider debouncing non-critical operations 7. Cleanup Resources: Stop listening when component is destroyed 8. Test Permissions: Verify microphone permissions before enabling the feature
Edge Cases
Rapid Start/Stop: Avoid calling startListening() immediately after stopListening():
// Wait for stop event before starting again
onListeningStop(args: StopListeningEventArgs): void {
setTimeout(() => {
// Safe to start again
}, 100);
}Error During Listening: Handle errors that may occur while actively listening:
onErrorHandler(args: ErrorEventArgs): void {
if (args.error === 'network') {
// Network connection lost
this.stopListening();
}
}Getting Started with SpeechToText Component
Table of Contents
- Installation
- Project Setup
- CSS Theme Import
- Module Import
- Basic Rendering
- Customizing Button Content
- Complete Example
Installation
The Syncfusion Angular SpeechToText component is part of the @syncfusion/ej2-angular-inputs package. Install it using npm:
Ivy Package (Angular 12+)
For modern Angular projects (version 12 and above), use the Ivy library distribution:
npm install @syncfusion/ej2-angular-inputs --saveThis command installs the package with Ivy distribution format, which provides optimal tree-shaking and smaller bundle sizes.
Project Setup
Ensure you have Angular CLI installed:
npm install -g @angular/cliCreate a new Angular application:
ng new my-app
cd my-appCSS Theme Import
The SpeechToText component depends on several Syncfusion packages. Add CSS imports to style.css to apply the theme:
@import '../node_modules/@syncfusion/ej2-base/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-inputs/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-buttons/styles/material3.css';
@import '../node_modules/@syncfusion/ej2-popups/styles/material3.css';Available Themes:
material3.css- Material Design 3 themetailwind3.css- Tailwind CSS themebootstrap5.css- Bootstrap 5 theme
Choose one theme based on your design requirements.
Module Import
Import the SpeechToTextModule in your component:
import { SpeechToTextModule } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `<button ejs-speechtotext></button>`
})
export class AppComponent { }Basic Rendering
The simplest way to render a SpeechToText component is to add the ejs-speechtotext directive to a button element:
import { Component } from '@angular/core';
import { SpeechToTextModule } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div style="width: 40px; margin: 50px auto;">
<button ejs-speechtotext></button>
</div>`
})
export class AppComponent { }Customizing Button Content
The button content can be customized for both active and inactive states:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs, ButtonSettingsModel } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[buttonSettings]="buttonSettings">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public buttonSettings: ButtonSettingsModel = {
content: 'Start Listening',
stopContent: 'Stop Listening'
};
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Complete Example
Here's a complete working example with all necessary setup:
app.component.ts:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext (transcriptChanged)="onTranscriptChange($event)"></button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}style.css:
@import 'node_modules/@syncfusion/ej2-base/styles/tailwind3.css';
@import 'node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css';
@import 'node_modules/@syncfusion/ej2-popups/styles/tailwind3.css';
@import 'node_modules/@syncfusion/ej2-angular-inputs/styles/tailwind3.css';
.speechText-container {
margin: 50px auto;
gap: 20px;
display: flex;
flex-direction: column;
align-items: center;
}index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<title>EJ2 Angular Speech To Text</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Angular Speech To Text Component" />
<meta name="author" content="Syncfusion" />
<link href="index.css" rel="stylesheet" />
</head>
<body>
<div id="wrapper">
<app-root>
<div id='loader'>LOADING....</div>
</app-root>
</div>
</body>
</html>Running the Application
Start the development server:
ng serveNavigate to http://localhost:4200/ in your browser. You should see the SpeechToText button component ready to capture speech input.
Requirements
- Browser Support: Speech Recognition API is required (Chrome 25+, Edge 79+, Safari 12+, Opera 30+)
- Active Internet Connection: Required for speech processing
- Microphone: Connected and available on the system
- User Permission: Grant microphone access when prompted by the browser
Internationalization
Table of Contents
- Localization Overview
- Localization Keys
- Setting Up Localization
- Multi-language Implementation
- RTL Support
- Complete Example
Localization Overview
The SpeechToText component supports localization for error messages, tooltips, and ARIA labels. By default, the component uses English (en-US). Use the L10n.load() method to provide translations for other languages.
Localization Keys
The following keys can be localized:
| Key | Default (en-US) | Purpose |
|---|---|---|
abortedError | Speech recognition was aborted. | Error message when recognition is aborted |
audioCaptureError | No microphone detected. Ensure your microphone is connected. | Microphone not found error |
defaultError | An unknown error occurred. | Generic error message |
networkError | Network error occurred. Check your internet connection. | Network connectivity error |
noSpeechError | No speech detected. Please speak into the microphone. | No audio input detected |
notAllowedError | Microphone access denied. Allow microphone permissions. | Permission denied error |
serviceNotAllowedError | Speech recognition service is not allowed in this context. | Service unavailable error |
unsupportedBrowserError | The browser does not support the SpeechRecognition API. | Browser incompatibility error |
startAriaLabel | Press to start speaking and transcribe your words | ARIA label for start state |
stopAriaLabel | Press to stop speaking and end transcription | ARIA label for stop state |
startTooltipText | Start listening | Tooltip for start state |
stopTooltipText | Stop listening | Tooltip for stop state |
Setting Up Localization
Use L10n.load() to register translation data. Call it in the component's ngOnInit() method:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
import { L10n } from '@syncfusion/ej2-base';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[locale]="'de'">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent implements OnInit {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
ngOnInit(): void {
// Load German localization
L10n.load({
'de': {
"speech-to-text": {
"abortedError": "Die Spracherkennung wurde abgebrochen.",
"audioCaptureError": "Kein Mikrofon erkannt. Stellen Sie sicher, dass Ihr Mikrofon angeschlossen ist.",
"defaultError": "Ein unbekannter Fehler ist aufgetreten.",
"networkError": "Netzwerkfehler aufgetreten. Überprüfen Sie Ihre Internetverbindung.",
"noSpeechError": "Keine Sprache erkannt. Bitte sprechen Sie in das Mikrofon.",
"notAllowedError": "Mikrofonzugriff verweigert. Erlauben Sie Mikrofonberechtigungen.",
"serviceNotAllowedError": "Der Spracherkennungsdienst ist in diesem Kontext nicht erlaubt.",
"unsupportedBrowserError": "Der Browser unterstützt die SpeechRecognition API nicht.",
"startAriaLabel": "Drücken Sie, um zu sprechen und Ihre Worte zu transkribieren",
"stopAriaLabel": "Drücken Sie, um das Sprechen zu beenden und die Transkription zu stoppen",
"startTooltipText": "Zuhören starten",
"stopTooltipText": "Zuhören beenden"
}
}
});
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Multi-language Implementation
Support multiple languages by loading translations for different locales:
import { Component, ViewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
import { L10n } from '@syncfusion/ej2-base';
@Component({
imports: [SpeechToTextModule, TextAreaModule, FormsModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<div class="language-selector">
<label>Select Language:</label>
<select [(ngModel)]="selectedLocale" (change)="onLanguageChange()">
<option value="en">English</option>
<option value="de">German (Deutsch)</option>
<option value="fr">French (Français)</option>
<option value="es">Spanish (Español)</option>
</select>
</div>
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[locale]="selectedLocale">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent implements OnInit {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public selectedLocale: string = 'en';
ngOnInit(): void {
// Load multiple language translations
L10n.load({
'de': {
"speech-to-text": {
"abortedError": "Die Spracherkennung wurde abgebrochen.",
"audioCaptureError": "Kein Mikrofon erkannt. Stellen Sie sicher, dass Ihr Mikrofon angeschlossen ist.",
"defaultError": "Ein unbekannter Fehler ist aufgetreten.",
"networkError": "Netzwerkfehler aufgetreten. Überprüfen Sie Ihre Internetverbindung.",
"noSpeechError": "Keine Sprache erkannt. Bitte sprechen Sie in das Mikrofon.",
"notAllowedError": "Mikrofonzugriff verweigert. Erlauben Sie Mikrofonberechtigungen.",
"serviceNotAllowedError": "Der Spracherkennungsdienst ist in diesem Kontext nicht erlaubt.",
"unsupportedBrowserError": "Der Browser unterstützt die SpeechRecognition API nicht.",
"startAriaLabel": "Drücken Sie, um zu sprechen",
"stopAriaLabel": "Drücken Sie, um zu stoppen",
"startTooltipText": "Zuhören starten",
"stopTooltipText": "Zuhören beenden"
}
},
'fr': {
"speech-to-text": {
"abortedError": "La reconnaissance vocale a été interrompue.",
"audioCaptureError": "Aucun microphone détecté. Assurez-vous que votre microphone est connecté.",
"defaultError": "Une erreur inconnue s'est produite.",
"networkError": "Erreur réseau. Vérifiez votre connexion Internet.",
"noSpeechError": "Aucune parole détectée. Veuillez parler dans le microphone.",
"notAllowedError": "Accès au microphone refusé. Autorisez les permissions du microphone.",
"serviceNotAllowedError": "Le service de reconnaissance vocale n'est pas autorisé dans ce contexte.",
"unsupportedBrowserError": "Le navigateur ne supporte pas l'API SpeechRecognition.",
"startAriaLabel": "Appuyez pour commencer à parler",
"stopAriaLabel": "Appuyez pour arrêter",
"startTooltipText": "Commencer à écouter",
"stopTooltipText": "Arrêter d'écouter"
}
},
'es': {
"speech-to-text": {
"abortedError": "El reconocimiento de voz fue cancelado.",
"audioCaptureError": "No se detectó micrófono. Asegúrese de que su micrófono está conectado.",
"defaultError": "Ocurrió un error desconocido.",
"networkError": "Error de red. Verifica tu conexión a Internet.",
"noSpeechError": "No se detectó voz. Por favor, hable en el micrófono.",
"notAllowedError": "Acceso al micrófono denegado. Permitir permisos de micrófono.",
"serviceNotAllowedError": "El servicio de reconocimiento de voz no está permitido en este contexto.",
"unsupportedBrowserError": "El navegador no es compatible con la API de SpeechRecognition.",
"startAriaLabel": "Presione para comenzar a hablar",
"stopAriaLabel": "Presione para detener",
"startTooltipText": "Comenzar a escuchar",
"stopTooltipText": "Dejar de escuchar"
}
}
});
}
onLanguageChange(): void {
console.log('Language changed to:', this.selectedLocale);
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}RTL Support
Enable Right-to-Left (RTL) text direction for languages like Arabic, Hebrew, and Persian using the enableRtl property:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs, ButtonSettingsModel } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container" [dir]="enableRtl ? 'rtl' : 'ltr'">
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[enableRtl]="enableRtl"
[buttonSettings]="buttonSettings">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public enableRtl: boolean = true; // Enable RTL
public buttonSettings: ButtonSettingsModel = {
content: 'ابدأ الاستماع', // Start Listening in Arabic
stopContent: 'إيقاف الاستماع' // Stop Listening in Arabic
};
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}RTL Considerations:
- Set
[dir]="rtl"on the container for proper text direction - Buttons and icons automatically reverse positioning
- Tooltips appear on the appropriate side
Complete Example
Here's a comprehensive localization setup with language switching:
import { Component, ViewChild, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
import { L10n } from '@syncfusion/ej2-base';
@Component({
imports: [SpeechToTextModule, TextAreaModule, FormsModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container" [dir]="isRtl ? 'rtl' : 'ltr'">
<h3>{{ 'title' | translate }}</h3>
<div class="language-selector">
<label>{{ 'selectLanguage' | translate }}:</label>
<select [(ngModel)]="selectedLocale" (change)="onLanguageChange()">
<option value="en">English</option>
<option value="de">Deutsch</option>
<option value="ar">العربية (RTL)</option>
</select>
</div>
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[locale]="selectedLocale"
[enableRtl]="isRtl">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text...">
</ejs-textarea>
</div>`
})
export class AppComponent implements OnInit {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public selectedLocale: string = 'en';
public isRtl: boolean = false;
ngOnInit(): void {
this.setupLocalizations();
}
private setupLocalizations(): void {
L10n.load({
'de': {
"speech-to-text": {
"abortedError": "Abgebrochen",
"audioCaptureError": "Kein Mikrofon",
"noSpeechError": "Keine Sprache erkannt",
"notAllowedError": "Berechtigung verweigert",
"startTooltipText": "Zuhören starten",
"stopTooltipText": "Zuhören beenden"
}
},
'ar': {
"speech-to-text": {
"abortedError": "تم الإلغاء",
"audioCaptureError": "لا يوجد ميكروفون",
"noSpeechError": "لم يتم اكتشاف كلام",
"notAllowedError": "تم رفض الإذن",
"startTooltipText": "ابدأ الاستماع",
"stopTooltipText": "إيقاف الاستماع"
}
}
});
}
onLanguageChange(): void {
this.isRtl = this.selectedLocale === 'ar';
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}CSS for RTL Support:
.speechText-container {
margin: 50px auto;
gap: 20px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 600px;
padding: 30px;
}
.speechText-container[dir="rtl"] {
direction: rtl;
text-align: right;
}
.speechText-container[dir="rtl"] .language-selector {
text-align: right;
}
.language-selector {
margin-bottom: 20px;
}
.language-selector select {
margin-left: 10px;
padding: 5px 10px;
}Best Practices
1. Load Translations Early: Call L10n.load() in ngOnInit() before rendering 2. Complete Translations: Provide all keys for consistency 3. RTL Testing: Test RTL languages thoroughly for UI layout 4. Locale Codes: Use standard locale codes (e.g., de, en, fr) 5. Fallback Language: Always provide English as a fallback 6. Context Awareness: Use appropriate language for speech recognition (lang property)
Security and Error Handling
Table of Contents
- Security Overview
- Online Dependency
- Security Risks
- Mitigation Strategies
- Error Types and Handling
- Error Handling Implementation
- Browser Support
Security Overview
The SpeechToText component relies on browser-based Speech Recognition APIs and external services. Understanding the security implications is crucial for protecting user privacy and data.
Online Dependency
The SpeechToText component requires an active internet connection and relies on third-party speech processing services (typically Google, Microsoft, or platform-specific engines). This introduces external dependencies that should be carefully managed.
// Check for internet connectivity before enabling speech recognition
checkInternetConnectivity(): boolean {
return navigator.onLine;
}Security Risks
Data Transmission to External Servers
Audio data is sent to third-party servers for processing and transcription:
Risks:
- Audio data transmission over the network
- Potential exposure to external entities
- Privacy implications if sensitive information is spoken
Mitigation:
- Use HTTPS for secure transmission
- Inform users about third-party data processing
- Request explicit user consent
Privacy Concerns
Speech services may store voice data for analytics, model improvement, or other purposes:
Risks:
- Voice data retention by service providers
- Potential misuse of stored data
- Privacy policy variations across providers
Mitigation:
- Review browser and service provider privacy policies
- Inform users about data retention practices
- Allow users to opt out where possible
Man-in-the-Middle (MITM) Attacks
Without HTTPS, attackers could intercept audio data during transmission:
Risks:
- Audio data interception
- Data modification during transit
- Unauthorized access to sensitive speech
Mitigation:
- Enforce HTTPS-only transmission
- Avoid HTTP fallbacks
- Use secure WebSocket connections
Browser and Permission Exploits
Malicious websites may attempt to misuse microphone permissions:
Risks:
- Unauthorized eavesdropping
- Voice data capture without consent
- Permission bypass attempts
Mitigation:
- Request microphone permissions explicitly
- Only enable when needed
- Revoke permissions after use
- Display clear permission prompts
Mitigation Strategies
1. Use HTTPS
Always use HTTPS to encrypt data transmission:
// Verify HTTPS context
if (window.location.protocol !== 'https:' && !this.isLocalhost()) {
console.warn('SpeechToText requires HTTPS for production');
}
private isLocalhost(): boolean {
return window.location.hostname === 'localhost' ||
window.location.hostname === '127.0.0.1';
}2. Inform Users
Clearly communicate about data processing:
// Display privacy notice before enabling speech input
displayPrivacyNotice(): void {
alert(`Speech data will be sent to processing servers for transcription.
Privacy policies apply. Please review before proceeding.`);
}3. Request Permissions Explicitly
Ask for microphone permission only when needed:
// Request microphone permission only on user action
async requestMicrophonePermission(): Promise<boolean> {
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true
});
// Stop the stream after checking permission
stream.getTracks().forEach(track => track.stop());
return true;
} catch (error) {
console.error('Microphone permission denied:', error);
return false;
}
}4. Revoke Permissions After Use
Stop microphone access when no longer needed:
onListeningStop(args: StopListeningEventArgs): void {
// Stop listening and close microphone connection
this.stopListening();
}
ngOnDestroy(): void {
// Clean up when component is destroyed
if (this.isListening) {
this.stopListening();
}
}5. Use Trusted Browsers
Support modern browsers with strong security features:
// Verify browser security compatibility
isBrowserSecure(): boolean {
const userAgent = navigator.userAgent;
const isModernBrowser =
userAgent.includes('Chrome') ||
userAgent.includes('Edge') ||
userAgent.includes('Safari');
return isModernBrowser;
}Error Types and Handling
The SpeechToText component can encounter various errors during speech recognition:
| Error | Code | Description | User Impact |
|---|---|---|---|
| No Speech | no-speech | Microphone detected no audio input | Ask user to speak clearly |
| Aborted | aborted | Recognition process was terminated | Inform user of interruption |
| Audio Capture | audio-capture | No microphone device detected | Check hardware connection |
| Not Allowed | not-allowed | Microphone permission denied | Request permission in settings |
| Service Not Allowed | service-not-allowed | Service unavailable in context | Contact support |
| Network | network | Network connectivity issue | Check internet connection |
| Unsupported Browser | unsupported-browser | Browser doesn't support API | Recommend compatible browser |
| Default | default | Unknown error occurred | Try again or contact support |
Error Handling Implementation
Basic Error Handling
import { Component } from '@angular/core';
import { SpeechToTextModule, ErrorEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext
(onError)="onErrorHandler($event)">
</button>
<p *ngIf="errorMessage" class="error-message">{{ errorMessage }}</p>
</div>`
})
export class AppComponent {
public errorMessage: string = '';
onErrorHandler(args: ErrorEventArgs): void {
this.handleError(args.error);
}
private handleError(error: string): void {
switch(error) {
case 'no-speech':
this.errorMessage = 'No speech detected. Please try again.';
break;
case 'audio-capture':
this.errorMessage = 'No microphone found. Please check your device.';
break;
case 'not-allowed':
this.errorMessage = 'Microphone permission denied. Please enable it in browser settings.';
break;
case 'network':
this.errorMessage = 'Network error. Please check your internet connection.';
break;
case 'unsupported-browser':
this.errorMessage = 'Your browser does not support speech recognition.';
break;
default:
this.errorMessage = 'An error occurred. Please try again.';
}
}
}Comprehensive Error Handler with Recovery
import { Component } from '@angular/core';
import { SpeechToTextModule, ErrorEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<div class="error-panel" *ngIf="showError">
<h4>{{ errorTitle }}</h4>
<p>{{ errorMessage }}</p>
<p class="error-code">Error: {{ errorCode }}</p>
<button (click)="dismissError()" class="dismiss-btn">Dismiss</button>
</div>
<button ejs-speechtotext
(onError)="onErrorHandler($event)"
[disabled]="isDisabled">
</button>
</div>`
})
export class AppComponent {
public showError: boolean = false;
public errorTitle: string = '';
public errorMessage: string = '';
public errorCode: string = '';
public isDisabled: boolean = false;
onErrorHandler(args: ErrorEventArgs): void {
this.displayError(args.error, args.errorMessage);
this.handleErrorRecovery(args.error);
}
private displayError(error: string, message: string): void {
this.errorCode = error;
this.showError = true;
const errorInfo = this.getErrorInfo(error);
this.errorTitle = errorInfo.title;
this.errorMessage = errorInfo.message;
}
private getErrorInfo(error: string): {title: string, message: string} {
const errorMap = {
'no-speech': {
title: 'No Speech Detected',
message: 'Please speak clearly into the microphone and try again.'
},
'audio-capture': {
title: 'Microphone Not Found',
message: 'Please connect a microphone and ensure it\'s properly configured.'
},
'not-allowed': {
title: 'Permission Denied',
message: 'Please enable microphone access in your browser settings.'
},
'network': {
title: 'Network Error',
message: 'Please check your internet connection and try again.'
},
'unsupported-browser': {
title: 'Browser Not Supported',
message: 'Please use Chrome, Edge, or Safari for speech recognition.'
},
'service-not-allowed': {
title: 'Service Unavailable',
message: 'Speech recognition service is not available. Please try later.'
},
'aborted': {
title: 'Recognition Aborted',
message: 'The recognition process was interrupted. Please try again.'
},
'default': {
title: 'Unknown Error',
message: 'An unexpected error occurred. Please try again.'
}
};
return errorMap[error] || errorMap['default'];
}
private handleErrorRecovery(error: string): void {
// Auto-dismiss non-critical errors
if (error === 'no-speech') {
setTimeout(() => this.dismissError(), 3000);
}
// Disable component for critical errors
if (error === 'unsupported-browser' || error === 'not-allowed') {
this.isDisabled = true;
}
}
dismissError(): void {
this.showError = false;
}
}Browser Support
The SpeechToText component relies on the Speech Recognition API. Support varies by browser:
| Browser | Version | Supported | Notes |
|---|---|---|---|
| Chrome | 25+ | ✓ Yes | Full support, well-tested |
| Edge | 79+ | ✓ Yes | Full support, Chromium-based |
| Firefox | All | ✗ No | Speech Recognition API not supported |
| Safari | 12+ | ✓ Yes | Good support on iOS and macOS |
| Opera | 30+ | ✓ Yes | Works on Chromium engine |
Browser Detection
checkBrowserSupport(): {supported: boolean, browser: string} {
const userAgent = navigator.userAgent;
if (userAgent.includes('Chrome') && !userAgent.includes('Edge')) {
return { supported: true, browser: 'Chrome' };
} else if (userAgent.includes('Edge')) {
return { supported: true, browser: 'Edge' };
} else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
return { supported: true, browser: 'Safari' };
} else if (userAgent.includes('Opera')) {
return { supported: true, browser: 'Opera' };
} else if (userAgent.includes('Firefox')) {
return { supported: false, browser: 'Firefox' };
}
return { supported: false, browser: 'Unknown' };
}Security Compliance Checklist
- [ ] HTTPS is enforced for production
- [ ] Users are informed about data processing
- [ ] Microphone permissions are requested explicitly
- [ ] Error handling covers all error types
- [ ] Error messages don't expose sensitive info
- [ ] Browser support is verified
- [ ] Permissions are revoked when not needed
- [ ] Privacy policies are accessible and clear
- [ ] Data transmission is encrypted
- [ ] Component is tested for security vulnerabilities
Best Practices
1. Always Use HTTPS: Encrypt all audio data transmission 2. Explicit Permission: Request microphone access only when needed 3. Clear Communication: Inform users about data processing 4. Error Recovery: Provide graceful fallbacks for errors 5. Permission Management: Revoke access when finished 6. Browser Verification: Check browser compatibility 7. Audit Logs: Track permission requests and errors 8. Regular Testing: Test error handling and recovery scenarios 9. Privacy Policy: Display clear, accessible privacy information 10. User Control: Allow users to opt out or delete data
Speech Recognition Features
Table of Contents
- Retrieving Transcripts
- Setting Language
- Allowing Interim Results
- Managing Listening State
- Showing or Hiding Tooltips
- Setting Disabled State
- Enabling State Persistence
Retrieving Transcripts
The transcript property contains the currently recognized text from speech input. Access it to get the transcribed content:
import { Component, ViewChild, ChangeDetectorRef } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule, FormsModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button #speechtotext
ejs-speechtotext
[transcript]="transcript"
(transcriptChanged)="onTranscriptChanged($event)">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
[(value)]="transcript"
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechtotext!: SpeechToTextComponent;
public transcript: string = 'Hi, hello! How are you?';
constructor(private cdr: ChangeDetectorRef) {}
onTranscriptChanged(args: TranscriptChangedEventArgs): void {
this.transcript = this.speechtotext.transcript;
this.cdr.detectChanges();
}
}Setting Language
The lang property specifies the language for speech recognition. Set it to the appropriate locale to ensure correct transcription:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Language Selection</h3>
<div class="language-selector">
<label>Select Language:</label>
<select [(ngModel)]="language" (change)="onLanguageChange()">
<option value="en-US">English (US)</option>
<option value="fr-FR">French</option>
<option value="de-DE">German</option>
<option value="es-ES">Spanish</option>
<option value="it-IT">Italian</option>
<option value="ja-JP">Japanese</option>
<option value="zh-CN">Chinese (Simplified)</option>
</select>
</div>
<button #speechtotext
ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[lang]="language">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public language: string = 'en-US';
onLanguageChange(): void {
console.log('Language changed to:', this.language);
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Supported Languages:
'en-US'- English (United States)'en-GB'- English (United Kingdom)'fr-FR'- French'de-DE'- German'es-ES'- Spanish'it-IT'- Italian'ja-JP'- Japanese'zh-CN'- Chinese (Simplified)'zh-TW'- Chinese (Traditional)'ru-RU'- Russian'pt-BR'- Portuguese (Brazil)
Allowing Interim Results
The allowInterimResults property controls whether interim (real-time) transcription is shown as the user speaks:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<div class="option-group">
<h4>Real-time Results</h4>
<label>
<input type="checkbox" [(ngModel)]="allowInterim" (change)="onInterimChange()">
Show interim results as you speak
</label>
</div>
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[allowInterimResults]="allowInterim">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public allowInterim: boolean = true; // Default: true
onInterimChange(): void {
console.log('Interim results:', this.allowInterim ? 'enabled' : 'disabled');
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Interim Results Explained:
true(default): Shows live transcription as the user speaks, updates continuouslyfalse: Waits until the user stops speaking, then shows only the final transcript
Managing Listening State
The listeningState property indicates the component's current status:
import { Component } from '@angular/core';
import { SpeechToTextModule, SpeechToTextState, StopListeningEventArgs, StartListeningEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule],
standalone: true,
selector: 'app-root',
template: `
<div id="container">
<div id="status-box-container" [ngClass]="'status-box ' + listeningStateClass">
<span>Status: <strong id="status-text">{{ listeningState }}</strong></span>
</div>
<button #speechtotext
ejs-speechtotext
(onStart)="updateListeningState($event)"
(onStop)="updateListeningState($event)"
[listeningState]="listeningState"
id="speechtotext_default">
</button>
<div class="waveform-container">
<div id="waveform-item" class="waveform" [style.display]="listeningState === 'Listening' ? 'flex' : 'none'">
<span></span><span></span><span></span><span></span><span></span>
</div>
<p id="instruction-text">{{ instructionText }}</p>
</div>
</div>`
})
export class AppComponent {
public listeningState: SpeechToTextState = SpeechToTextState.Inactive;
public listeningStateClass: string = 'inactive';
public instructionText: string = 'Click the button to start listening.';
updateListeningState(args: StartListeningEventArgs | StopListeningEventArgs): void {
const state = args.listeningState;
this.listeningState = state as SpeechToTextState;
if (state === "Listening") {
this.listeningStateClass = "listening";
this.instructionText = "Listening... Speak now!";
} else if (state === "Stopped") {
this.listeningStateClass = "stopped";
this.instructionText = "Recognition Stopped.";
} else {
this.listeningStateClass = "inactive";
this.instructionText = "Click the button to start listening.";
}
}
}Listening States:
'Inactive'- Component is ready but not listening'Listening'- Actively capturing and processing speech'Stopped'- Recognition has been terminated
CSS for State Visualization:
#container {
text-align: center;
margin: 50px auto;
max-width: 400px;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
background: #fff;
}
.status-box {
padding: 10px;
border-radius: 5px;
margin-bottom: 40px;
font-weight: bold;
}
.status-box.listening {
background-color: #d1e7dd;
color: #0f5132;
}
.status-box.stopped {
background-color: #f8d7da;
color: #842029;
}
.status-box.inactive {
background-color: #e2e3e5;
color: #6c757d;
}
.waveform-container {
margin-top: 20px;
font-weight: bold;
}
.waveform {
display: flex;
justify-content: center;
align-items: center;
height: 40px;
gap: 5px;
}
.waveform span {
display: block;
width: 6px;
height: 20px;
background: #28a745;
animation: wave-animation 1.2s infinite ease-in-out;
}
.waveform span:nth-child(1) { animation-delay: 0s; }
.waveform span:nth-child(2) { animation-delay: 0.2s; }
.waveform span:nth-child(3) { animation-delay: 0.4s; }
.waveform span:nth-child(4) { animation-delay: 0.6s; }
.waveform span:nth-child(5) { animation-delay: 0.8s; }
@keyframes wave-animation {
0%, 100% { height: 10px; }
50% { height: 30px; }
}Showing or Hiding Tooltips
The showTooltip property controls whether tooltips appear on hover:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<div class="option-group">
<h4>Tooltip Settings</h4>
<label>
<input type="checkbox" [(ngModel)]="showTooltip" (change)="onTooltipChange()">
Show tooltips on hover
</label>
</div>
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[showTooltip]="showTooltip">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public showTooltip: boolean = true; // Default: true
onTooltipChange(): void {
console.log('Tooltips:', this.showTooltip ? 'shown' : 'hidden');
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Setting Disabled State
The disabled property prevents user interaction with the component:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<div class="option-group">
<h4>Component State</h4>
<label>
<input type="checkbox" [(ngModel)]="isDisabled" (change)="onDisabledChange()">
Disable speech input
</label>
</div>
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[disabled]="isDisabled">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public isDisabled: boolean = false; // Default: false
onDisabledChange(): void {
console.log('Component:', this.isDisabled ? 'disabled' : 'enabled');
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Enabling State Persistence
The enablePersistence property allows the component to maintain its state across page reloads or browser sessions:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>State Persistence Example</h3>
<p>The transcript will be preserved even after page reload.</p>
<button ejs-speechtotext
id="persistentSpeech"
(transcriptChanged)="onTranscriptChange($event)"
[enablePersistence]="true">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
<button class="e-btn" (click)="reloadPage()">
Reload Page (State will persist)
</button>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
reloadPage(): void {
window.location.reload();
}
}How State Persistence Works:
- When
enablePersistenceistrue, the component automatically saves its state to browser's localStorage - State includes: transcript value, listening state, and other configuration
- An
idattribute must be provided on the component for persistence to work - The persisted state is restored when the component is re-initialized
Custom State Management Example:
If you need more control over what gets persisted, you can implement custom state management:
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Custom State Persistence</h3>
<button #speechtotext
ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)">
</button>
<ejs-textarea #outputTextarea
[(value)]="transcript"
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
<div class="action-buttons">
<button class="e-btn e-primary" (click)="saveState()">
Save State
</button>
<button class="e-btn" (click)="clearState()">
Clear Saved State
</button>
</div>
</div>`
})
export class AppComponent implements OnInit, OnDestroy {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public transcript: string = '';
private readonly STORAGE_KEY = 'speechtotext-state';
ngOnInit(): void {
// Restore state on component initialization
this.loadState();
}
ngOnDestroy(): void {
// Optionally save state when component is destroyed
this.saveState();
}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.transcript = args.transcript;
// Auto-save on every transcript change
this.saveState();
}
saveState(): void {
const state = {
transcript: this.transcript,
timestamp: new Date().toISOString()
};
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(state));
console.log('State saved successfully');
}
loadState(): void {
const savedState = localStorage.getItem(this.STORAGE_KEY);
if (savedState) {
try {
const state = JSON.parse(savedState);
this.transcript = state.transcript || '';
console.log('State restored from:', state.timestamp);
} catch (error) {
console.error('Failed to restore state:', error);
}
}
}
clearState(): void {
localStorage.removeItem(this.STORAGE_KEY);
this.transcript = '';
console.log('State cleared');
}
}Best Practices for State Persistence:
1. Always Provide an ID: The component needs a unique id attribute for built-in persistence 2. Consider Data Size: Don't persist large amounts of data; localStorage has size limits 3. Privacy Concerns: Inform users if transcripts are being saved locally 4. Session vs Persistent Storage: Use sessionStorage for temporary persistence within a browser session 5. Clear Old Data: Implement cleanup logic to remove stale persisted data 6. Error Handling: Always wrap persistence operations in try-catch blocks
Practical Example: Complete Feature Configuration
import { Component, ViewChild, ChangeDetectorRef } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { SpeechToTextModule, SpeechToTextComponent, TextAreaModule, TranscriptChangedEventArgs, SpeechToTextState } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule, FormsModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Advanced Speech Recognition Features</h3>
<div class="settings-panel">
<div class="setting-group">
<label>Language:</label>
<select [(ngModel)]="language">
<option value="en-US">English</option>
<option value="fr-FR">French</option>
<option value="de-DE">German</option>
</select>
</div>
<div class="setting-group checkbox">
<label>
<input type="checkbox" [(ngModel)]="allowInterim">
Show interim results
</label>
</div>
<div class="setting-group checkbox">
<label>
<input type="checkbox" [(ngModel)]="showTooltip">
Show tooltips
</label>
</div>
</div>
<div class="status-display">
<p>Status: <strong>{{ listeningState }}</strong></p>
<p>Language: <strong>{{ language }}</strong></p>
</div>
<button #speechtotext
ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[lang]="language"
[allowInterimResults]="allowInterim"
[showTooltip]="showTooltip"
[listeningState]="listeningState">
</button>
<ejs-textarea [(value)]="transcript"
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('speechtotext') speechToText!: SpeechToTextComponent;
public language: string = 'en-US';
public allowInterim: boolean = true;
public showTooltip: boolean = true;
public transcript: string = '';
public listeningState: SpeechToTextState = SpeechToTextState.Inactive;
constructor(private cdr: ChangeDetectorRef) {}
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.transcript = this.speechToText.transcript;
this.cdr.detectChanges();
}
}Best Practices
1. Language Selection: Allow users to choose their preferred language 2. Interim Results: Enable for real-time feedback; disable for cleaner final output 3. Tooltip Clarity: Provide helpful tooltips for first-time users 4. State Monitoring: Track listening state for UI updates 5. Transcript Handling: Process transcripts carefully, check for empty strings 6. Disable When Inappropriate: Disable during processing or when microphone unavailable
Styling and Appearance
Table of Contents
- CSS Class System
- CSS Classes Overview
- Custom Styling with cssClass
- Theme Configuration
- HTML Attributes
- Complete Styling Example
CSS Class System
The SpeechToText component uses Syncfusion's CSS class system to provide consistent, theme-aware styling. These predefined classes control the button's appearance and state.
CSS Classes Overview
| cssClass | Description | Use Case |
|---|---|---|
e-primary | Represents a primary action | Call-to-action buttons |
e-outline | Renders with outline/border style | Secondary actions |
e-info | Informative action styling | Help or information buttons |
e-success | Positive action styling | Confirmation or success actions |
e-warning | Warning state styling | Cautionary actions |
e-danger | Destructive/negative action | Dangerous or irreversible actions |
Custom Styling with cssClass
Apply predefined CSS classes using the cssClass property:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Styled SpeechToText Components</h3>
<!-- Primary button -->
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
cssClass="e-primary">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Theme Configuration
Import the appropriate theme CSS file in your style.css:
Material Design 3 Theme
@import 'node_modules/@syncfusion/ej2-base/styles/material3.css';
@import 'node_modules/@syncfusion/ej2-buttons/styles/material3.css';
@import 'node_modules/@syncfusion/ej2-popups/styles/material3.css';
@import 'node_modules/@syncfusion/ej2-angular-inputs/styles/material3.css';Tailwind CSS Theme
@import 'node_modules/@syncfusion/ej2-base/styles/tailwind3.css';
@import 'node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css';
@import 'node_modules/@syncfusion/ej2-popups/styles/tailwind3.css';
@import 'node_modules/@syncfusion/ej2-angular-inputs/styles/tailwind3.css';Bootstrap 5 Theme
@import 'node_modules/@syncfusion/ej2-base/styles/bootstrap5.css';
@import 'node_modules/@syncfusion/ej2-buttons/styles/bootstrap5.css';
@import 'node_modules/@syncfusion/ej2-popups/styles/bootstrap5.css';
@import 'node_modules/@syncfusion/ej2-angular-inputs/styles/bootstrap5.css';HTML Attributes
Use the htmlAttributes property to set custom HTML attributes on the button element:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<button ejs-speechtotext
(transcriptChanged)="onTranscriptChange($event)"
[htmlAttributes]="customAttributes">
</button>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public customAttributes = {
'data-testid': 'speech-button',
'aria-label': 'Start speech recognition',
'title': 'Click to begin voice input'
};
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}Complete Styling Example
Here's a comprehensive example combining themes, CSS classes, and custom styling:
import { Component, ViewChild } from '@angular/core';
import { SpeechToTextModule, TextAreaComponent, TextAreaModule, TranscriptChangedEventArgs, ButtonSettingsModel } from '@syncfusion/ej2-angular-inputs';
@Component({
imports: [SpeechToTextModule, TextAreaModule],
standalone: true,
selector: 'app-root',
template: `
<div class="speechText-container">
<h3>Fully Styled SpeechToText Components</h3>
<!-- Primary button -->
<div class="button-group">
<h4>Primary Button</h4>
<button ejs-speechtotext
cssClass="e-primary"
(transcriptChanged)="onTranscriptChange($event)"
[buttonSettings]="primaryButton">
</button>
</div>
<!-- Outline button -->
<div class="button-group">
<h4>Outline Button</h4>
<button ejs-speechtotext
cssClass="e-outline"
(transcriptChanged)="onTranscriptChange($event)"
[buttonSettings]="outlineButton">
</button>
</div>
<!-- Success button -->
<div class="button-group">
<h4>Success Button</h4>
<button ejs-speechtotext
cssClass="e-success"
(transcriptChanged)="onTranscriptChange($event)"
[buttonSettings]="successButton">
</button>
</div>
<!-- Warning button -->
<div class="button-group">
<h4>Warning Button</h4>
<button ejs-speechtotext
cssClass="e-warning"
(transcriptChanged)="onTranscriptChange($event)">
</button>
</div>
<ejs-textarea #outputTextarea
id="textareaInst"
value=""
rows="5"
cols="50"
resizeMode="None"
placeholder="Transcribed text will be shown here...">
</ejs-textarea>
</div>`
})
export class AppComponent {
@ViewChild('outputTextarea') outputTextarea!: TextAreaComponent;
public primaryButton: ButtonSettingsModel = {
content: 'Listen',
stopContent: 'Stop',
isPrimary: true
};
public outlineButton: ButtonSettingsModel = {
content: 'Speak',
stopContent: 'Recording...'
};
public successButton: ButtonSettingsModel = {
content: 'Start',
stopContent: 'Listening...'
};
onTranscriptChange(args: TranscriptChangedEventArgs): void {
this.outputTextarea.value = args.transcript;
}
}CSS Styling:
.speechText-container {
margin: 50px auto;
gap: 30px;
display: flex;
flex-direction: column;
align-items: center;
max-width: 700px;
padding: 30px;
background-color: #f5f5f5;
border-radius: 8px;
}
.speechText-container h3 {
color: #333;
font-size: 28px;
margin-bottom: 20px;
}
.button-group {
text-align: center;
padding: 20px;
background-color: white;
border-radius: 6px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
width: 100%;
}
.button-group h4 {
color: #666;
margin-bottom: 15px;
font-size: 14px;
}
/* Custom button styling */
.speechText-container .e-speech-to-text {
margin: 10px 0;
padding: 12px 24px;
border-radius: 4px;
font-size: 16px;
font-weight: 500;
}
/* Responsive design */
@media (max-width: 600px) {
.speechText-container {
margin: 20px auto;
padding: 15px;
}
.speechText-container h3 {
font-size: 20px;
}
.button-group {
padding: 15px;
}
}Best Practices
1. Choose Appropriate Classes: Use e-primary for main actions, e-outline for secondary 2. Consistent Theming: Select one theme and stick with it across the application 3. Accessibility: Use sufficient color contrast; don't rely only on color 4. Mobile Responsive: Test styling on different screen sizes 5. Performance: Minimize custom CSS overrides 6. Semantic HTML Attributes: Use meaningful data attributes for testing and accessibility
Edge Cases
Multiple CSS Classes: You can apply multiple classes by separating them with spaces:
cssClass="e-primary customClass"Dynamic Class Changes: Change CSS classes dynamically based on state:
toggleClass(): void {
if (this.isPrimary) {
this.buttonClass = 'e-outline';
this.isPrimary = false;
} else {
this.buttonClass = 'e-primary';
this.isPrimary = true;
}
}Override Theme Colors: Create custom CSS to override theme defaults:
.speechText-container .e-speech-to-text.e-primary {
background-color: #custom-color;
border-color: #custom-border-color;
}