DevExtreme v26.1 is now available.

Explore our newest features/capabilities and share your thoughts with us.

Your search did not match any results.

Angular Speech To Text

The DevExtreme SpeechToText component allows you to integrate voice input into your DevExtreme-powered app. The component implements the Web Speech API SpeechRecognition interface and supports custom speech recognizers.

Backend API
<div class="speech-to-text-demo"> <div class="speech-to-text-container"> <span>Use voice recognition (speech to text)</span> <dx-speech-to-text id="speech-to-text" [class.animation-disabled]="!animation" [class.custom-button]="displayMode === 'Custom'" [startText]="startText" [stopText]="stopText" [stylingMode]="stylingMode" [type]="type" [hint]="hint" [disabled]="disabled" [speechRecognitionConfig]="speechRecognitionConfig" (onStartClick)="onStartClick()" (onResult)="onResult($event)" (onEnd)="onEnd()" ></dx-speech-to-text> <dx-text-area id="text-area" [(value)]="textAreaValue" [width]="360" [height]="120" placeholder="Recognized text will appear here..." [inputAttr]="{ 'aria-label': 'Recognized Text' }" ></dx-text-area> <dx-button text="Clear" [disabled]="textAreaValue === ''" (onClick)="onClearButtonClick()" ></dx-button> </div> <div class="options"> <div class="caption">Options</div> <div class="option"> <div>Display Mode</div> <dx-select-box [(value)]="displayMode" [dataSource]="displayModes" [inputAttr]="{ 'aria-label': 'Display Mode' }" (onValueChanged)="onDisplayModeChanged($event)" ></dx-select-box> </div> <div class="option"> <div>Styling Mode</div> <dx-select-box [(value)]="stylingMode" [dataSource]="stylingModes" displayExpr="displayValue" valueExpr="value" [disabled]="displayMode === 'Custom'" [inputAttr]="{ 'aria-label': 'Styling Mode' }" ></dx-select-box> </div> <div class="option"> <div>Type</div> <dx-select-box [(value)]="type" [dataSource]="types" displayExpr="displayValue" valueExpr="value" [disabled]="displayMode === 'Custom'" [inputAttr]="{ 'aria-label': 'Type' }" ></dx-select-box> </div> <div class="switch"> <dx-switch [(value)]="disabled"></dx-switch> <span>Disabled</span> </div> <div class="option-separator"></div> <div class="option"> <div>Language</div> <dx-select-box [(value)]="language" [dataSource]="languages" [inputAttr]="{ 'aria-label': 'Language' }" (onValueChanged)="updateSpeechRecognitionConfig()" ></dx-select-box> </div> <div class="switch"> <dx-switch [(value)]="interimResults" (onValueChanged)="updateSpeechRecognitionConfig()" ></dx-switch> <span>Interim Results</span> </div> <div class="switch"> <dx-switch [(value)]="continuous" (onValueChanged)="updateSpeechRecognitionConfig()" ></dx-switch> <span>Continuous Recognition</span> </div> <div class="option-separator"></div> <div class="switch"> <dx-switch [(value)]="animation"></dx-switch> <span>Animation</span> </div> </div> </div>
import { bootstrapApplication } from '@angular/platform-browser'; import { Component, enableProdMode, provideZoneChangeDetection } from '@angular/core'; import { DxSpeechToTextModule } from 'devextreme-angular/ui/speech-to-text'; import { DxTextAreaModule } from 'devextreme-angular/ui/text-area'; import { DxButtonModule } from 'devextreme-angular/ui/button'; import { DxSelectBoxModule } from 'devextreme-angular/ui/select-box'; import { DxSwitchModule } from 'devextreme-angular/ui/switch'; import notify from 'devextreme/ui/notify'; import { AppService } from './app.service'; declare global { interface Window { SpeechRecognition?: any; webkitSpeechRecognition?: any; } } if (!/localhost/.test(document.location.host)) { enableProdMode(); } @Component({ selector: 'demo-app', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], imports: [ DxSpeechToTextModule, DxTextAreaModule, DxButtonModule, DxSelectBoxModule, DxSwitchModule, ], }) export class AppComponent { state: 'initial' | 'listening'; startText: string; stopText: string; displayMode: string; displayModes: string[]; stylingMode: string; stylingModes: { displayValue: string; value: string }[]; type: string; types: { displayValue: string; value: string }[]; hint: string; disabled: boolean; textAreaValue: string; language: string; languages: string[]; langMap: { [key: string]: string }; interimResults: boolean; continuous: boolean; animation: boolean; speechRecognitionConfig: Record<string, unknown>; constructor(private readonly appService: AppService) { this.state = 'initial'; this.startText = ''; this.stopText = ''; this.displayModes = this.appService.getDisplayModes(); this.displayMode = this.displayModes[0]; this.stylingModes = this.appService.getStylingModes(); this.stylingMode = this.stylingModes[0].value; this.types = this.appService.getTypes(); this.type = this.types[2].value; this.hint = 'Start voice recognition'; this.disabled = false; this.textAreaValue = ''; this.languages = this.appService.getLanguages(); this.language = this.languages[0]; this.langMap = this.appService.getLangMap(); this.interimResults = true; this.continuous = false; this.animation = true; this.updateSpeechRecognitionConfig(); } onStartClick() { if (!window.SpeechRecognition && !window.webkitSpeechRecognition) { notify({ message: 'The browser does not support Web Speech API (SpeechRecognition).', type: 'error', displayTime: 7000, position: 'bottom center', width: 'auto', }); return; } this.state = 'listening'; this.hint = 'Stop voice recognition'; if (this.displayMode !== 'Custom') { return; } this.type = 'danger'; } onResult({ event }) { const { results } = event; const resultText = Object.values(results) .map((resultItem) => resultItem[0].transcript.trim()) .join(' '); this.textAreaValue = resultText; } onEnd() { this.state = 'initial'; this.hint = 'Start voice recognition'; if (this.displayMode !== 'Custom') { return; } this.type = 'default'; } onClearButtonClick() { this.textAreaValue = ''; } onDisplayModeChanged({ value }) { if (value === 'Text and Icon') { this.startText = 'Dictate'; this.stopText = 'Stop'; return; } this.startText = ''; this.stopText = ''; if (value === 'Custom') { this.stylingMode = 'contained'; this.type = this.state === 'initial' ? 'default' : 'danger'; } } updateSpeechRecognitionConfig() { this.speechRecognitionConfig = { lang: this.langMap[this.language], interimResults: this.interimResults, continuous: this.continuous, }; } } bootstrapApplication(AppComponent, { providers: [ provideZoneChangeDetection({ eventCoalescing: true, runCoalescing: true }), AppService, ], });
::ng-deep .speech-to-text-demo { display: flex; gap: 20px; height: 640px; } ::ng-deep .speech-to-text-container { display: flex; flex-direction: column; row-gap: 16px; flex-grow: 1; align-items: center; justify-content: center; } ::ng-deep #text-area { margin-top: 16px; } ::ng-deep .options { display: flex; flex-direction: column; flex-shrink: 0; width: 300px; box-sizing: border-box; padding: 20px; background-color: rgba(191, 191, 191, 0.15); gap: 16px; } ::ng-deep .caption { font-weight: 500; font-size: 18px; } ::ng-deep .option { display: flex; flex-direction: column; row-gap: 4px; } ::ng-deep .switch { display: flex; align-items: center; column-gap: 8px; } ::ng-deep .option-separator { border-bottom: 1px solid var(--dx-color-border); } ::ng-deep #speech-to-text.animation-disabled { animation: none; } ::ng-deep #speech-to-text.custom-button { border-radius: 2rem; }
import { Injectable } from '@angular/core'; import { type DxButtonTypes } from 'devextreme-angular/ui/button'; @Injectable({ providedIn: 'root', }) export class AppService { displayModes: string[] = []; stylingModes: { displayValue: string; value: DxButtonTypes.ButtonStyle }[] = []; types: { displayValue: string; value: DxButtonTypes.ButtonType }[] = []; languages: string[] = []; langMap: { [key: string]: string } = {}; constructor() { this.displayModes = ['Icon Only', 'Text and Icon', 'Custom']; this.stylingModes = [ { displayValue: 'Contained', value: 'contained', }, { displayValue: 'Outlined', value: 'outlined', }, { displayValue: 'Text', value: 'text', }, ]; this.types = [ { displayValue: 'Normal', value: 'normal', }, { displayValue: 'Success', value: 'success', }, { displayValue: 'Default', value: 'default', }, { displayValue: 'Danger', value: 'danger', }, ]; this.languages = [ 'Auto-detect', 'English', 'Spanish', 'French', 'German', ]; this.langMap = { 'Auto-detect': '', 'English': 'en-US', 'Spanish': 'es-ES', 'French': 'fr-FR', 'German': 'de-DE', }; } getDisplayModes() { return this.displayModes; } getStylingModes() { return this.stylingModes; } getTypes() { return this.types; } getLanguages() { return this.languages; } getLangMap() { return this.langMap; } }
<!DOCTYPE html> <html lang="en"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/26.1.5/css/dx.light.css" /> </head> <body class="dx-viewport"> <div class="demo-container"> <demo-app>Loading...</demo-app> </div> <script src="https://cdn.jsdelivr.net/npm/zone.js@0.16.2/bundles/zone.umd.js"></script> <script src="./bundle.js" type="module"></script> </body> </html>

You can integrate SpeechToText with any text input, including editors available in the DevExtreme product line. Simply set a component's value property to transcribed text. SpeechToText returns transcribed text in the onResult handler as a user speaks. When speech stops, the component calls the onEnd handler and switches from a "listening" state to the initial state. SpeechToText implements state-dependent icons (startIcon/stopIcon), text (startText/stopText), and click handlers (onStartClick/onStopClick).

For a complete overview of SpeechToText options (including Web Speech API options), refer to the following topic: SpeechToText API Reference.