New to Kendo UI for Vue? Start a free 30-day trial

SpeechToTextButton Integration

Updated on Nov 11, 2026

The Kendo UI for Vue SpeechToTextButton provides flexible integration options for speech recognition. By default, it uses the browser's native Web Speech API, which provides a seamless out-of-the-box experience. This enables you to pass the transcript to other components, such as a text input, and create powerful voice-enabled user interfaces.

Default Integration with the Web Speech API

The SpeechToTextButton works immediately without any additional configuration—the browser recognition engine starts when the user activates the button. Simply add the component to your template and handle onResult to process recognized speech.

vue
<script setup>
import { ref } from 'vue';
import { SpeechToTextButton } from '@progress/kendo-vue-buttons';

const textAreaValue = ref('');

const onResult = (event) => {
    if (event.alternatives.length > 0) {
        textAreaValue.value += event.alternatives[0].transcript + ' ';
    }
};
</script>

<template>
    <SpeechToTextButton @result="onResult" />
</template>

The Web Speech API provides:

  • Built-in speech recognition without external dependencies
  • Support for multiple languages
  • Real-time processing
  • Interim results capability
  • Multiple recognition alternatives

For details on enabling features such as continuous recognition, interim results, and changing the recognition language, refer to the Configuration Options documentation.

TextArea Integration

The following example demonstrates the default Web Speech API integration. It adds the SpeechToTextButton to a TextArea suffix and sends final recognition results to the field.

Example
View Source
Loading ...

External Service Integration

For scenarios requiring custom speech recognition providers, you can integrate the component with third-party services by setting the integrationMode prop to None.

This approach is ideal when you want to:

  • Use a cloud-based speech recognition service (such as Azure, Google, or AWS).
  • Integrate with an on-premises or proprietary speech-to-text engine.
  • Implement custom business logic for processing audio input.

How It Works

When integrationMode is set to None, the SpeechToTextButton disables its built-in speech recognition. Instead, you can handle the button's events to implement your own speech recognition logic. Typically, you will:

  1. Capture audio from the user (using the browser's APIs or a custom solution).
  2. Send the audio data to your chosen speech-to-text provider.
  3. Process the provider's response and update your UI accordingly.

When the SpeechToTextButton is used for the first time, the browser will prompt the user for permission to access the microphone. This is required for capturing audio input. Users must grant permission for speech recognition to function.

Integration Flow

Use the onStart and onEnd callbacks, so you can handle audio capture and speech recognition entirely within your own logic.

  1. Use onStart to begin capturing audio from the user.
  2. Use onEnd to stop audio capture and send the recorded audio to your chosen speech-to-text provider.
  3. Process the provider's response and update your UI with the recognized text.

All result handling and UI updates should be implemented in your custom event handlers, as the component will not emit recognition results when using third-party or custom integrations.

Implementation Example

The following example integrates the SpeechToTextButton with the Google Cloud Speech-to-Text API by using async event handlers to record audio and send it to the Google Cloud service. To test the example, copy it locally and set your API key to the GOOGLE_API_KEY variable. In production, call the API through a server so the key is not exposed in the browser.

vue
<template>
  <div>
    <SpeechToTextButton
      integration-mode="None"
      @start="onStart"
      @end="onEnd"
    />
    <div v-if="transcript" class="transcript">
      {{ transcript }}
    </div>
    <div class="log-area">
      <div v-for="(log, idx) in logs" :key="idx" class="log-entry">{{ log }}</div>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { SpeechToTextButton } from '@progress/kendo-vue-buttons';

const mediaRecorder = ref(null);
const audioChunks = ref([]);
const transcript = ref('');
const logs = ref([]);

function addLog(message) {
    logs.value.push(message);
}

const onStart = async () => {
    addLog('Speech-to-Text process started');
    await startRecording();
};
const onEnd = async () => {
    await stopRecording();
    addLog('Speech-to-Text process stopped');
};

const startRecording = async () => {
    addLog('Starting recording...');
    try {
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        mediaRecorder.value = new MediaRecorder(stream);
        audioChunks.value = [];

        mediaRecorder.value.ondataavailable = (event) => {
            audioChunks.value.push(event.data);
        };

        mediaRecorder.value.start();

    } catch (error) {
        addLog('Error starting recording: ' + error.message);
        handleIntegrationError({ errorMessage: error.message });
    }
};

const stopRecording = async () => {
    addLog('Stopping recording...');
    const recorder = mediaRecorder.value;
    if (!recorder || recorder.state !== 'recording') {
        return;
    }

    const stopped = new Promise((resolve) => {
        recorder.addEventListener('stop', resolve, { once: true });
    });
    recorder.stop();
    await stopped;

    const audioBlob = new Blob(audioChunks.value, { type: recorder.mimeType });
    recorder.stream.getTracks().forEach(track => track.stop());
    await sendToSpeechService(audioBlob);
};

const sendToSpeechService = async (audioBlob) => {
    addLog('Sending audio to the speech service...');

    try {
        const response = await fetch('/api/speech-to-text', {
            method: 'POST',
            headers: { 'Content-Type': audioBlob.type },
            body: audioBlob
        });

        if (!response.ok) {
            throw new Error('Speech service request failed.');
        }

        const result = await response.json();
        transcript.value = result.transcript;
    } catch (error) {
        addLog('Error calling speech service: ' + error.message);
        handleIntegrationError({ errorMessage: error.message });
    }
};

const handleIntegrationError = (event) => {
    addLog('Speech-to-text integration error: ' + event.errorMessage);
};
</script>

<style scoped>
.log-area {
  margin-top: 1rem;
  padding: 0.5rem;
  border: 1px solid #eee;
  background: #fafbfc;
  font-size: 0.95em;
  max-height: 200px;
  overflow-y: auto;
}
.log-entry {
  margin-bottom: 0.25rem;
  color: #555;
}
</style>

When integrating with third-party or custom speech-to-text providers (integration-mode="None"), you are responsible for:

  • Capturing and recording audio from the user.
  • Converting the audio to the format required by your provider (for example, WAV/PCM).
  • Handling authentication (such as API keys).
  • Sending the audio to the provider's API and processing the response.

Refer to your provider's documentation for details on supported formats and API usage.

Browser Support and Microphone Permissions

Web Speech API support varies by browser and environment. For the latest browser compatibility, refer to the Web Speech API browser compatibility table. The component checks for the SpeechRecognition or webkitSpeechRecognition browser API. If neither exists, it reports an unsupported-browser message through onError when you provide that callback.

The browser controls microphone permission for speech recognition. Handle permission or recognition errors with onError. If your custom integration captures audio with getUserMedia(), run it in a secure context and handle microphone permission separately.