API Reference / React InstantSearch Widgets / VoiceSearch
Apr. 24, 2019

VoiceSearch

Widget signature
<VoiceSearch
  // Optional parameters
  searchAsYouSpeak={boolean}
  buttonTextComponent={React.Node}
  statusComponent={React.Node}
  translations={object}
/>

About this widget

The VoiceSearch widget lets the user perform a voice-based query.

It uses the Web Speech API, which only Chrome (from version 25) has implemented so far. This means the voiceSearch widget only works on desktop Chrome and Android Chrome. It doesn’t work on iOS Chrome, which uses the iOS WebKit.

Examples

1
2
3
import { VoiceSearch } from 'react-instantsearch-dom';

<VoiceSearch />

Props

searchAsYouSpeak
type: boolean
default: false
Optional

Whether or not to trigger the search as you speak. If false, search is triggered only after speech is finished. If true, search is triggered whenever the engine delivers an interim transcript.

1
<VoiceSearch searchAsYouSpeak={false} />
buttonTextComponent
type: React.Node
Optional

Changes the content of the default button (mic icon). It receives the following properties:

  • status: string: current status (initial|askingPermission| waiting|recognizing|finished|error).
  • transcript: string: currently recognized transcript.
  • isSpeechFinal: boolean: true if speech recognition is finished.
  • errorCode: string|undefined: an error code (if any). Refer to the spec for more information.
  • isListening: boolean: true if listening to user’s speech.
  • isBrowserSupported: boolean: true if user’s browser supports voice search.
1
2
3
4
5
6
7
8
9
10
11
12
const ButtonText = ({
  status,
  transcript,
  isSpeechFinal,
  errorCode,
  isListening,
  isBrowserSupported,
}) => (isListening ? '' : '🎙');

<VoiceSearch
  buttonTextComponent={ButtonText}
/>
statusComponent
type: React.Node
Optional

Changes the content of the status. It receives the same properties as above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const Status = ({
  status,
  transcript,
  isSpeechFinal,
  errorCode,
  isListening,
  isBrowserSupported,
}) => {
  return (
    <div>
      <p>status: {status}</p>
      <p>errorCode: {errorCode}</p>
      <p>isListening: {isListening ? 'true' : 'false'}</p>
      <p>transcript: {transcript}</p>
      <p>isSpeechFinal: {isSpeechFinal ? 'true' : 'false'}</p>
      <p>isBrowserSupported: {isBrowserSupported ? 'true' : 'false'}</p>
    </div>
  );
};

<VoiceSearch
  statusComponent={Status}
/>
translations
type: object
Optional

A mapping of keys to translation values.

  • buttonTitle: The title attribute on the button
  • disabledButtonTitle: The title attribute on the button when it’s disabled on unsupported browsers
1
2
3
4
5
6
<VoiceSearch
  translations={{
    buttonTitle: 'Voice Search',
    disabledButtonTitle: 'Voice Search Disabled',
  }}
/>

Customize the UI - connectSearchBox

If you want to create your own UI of the VoiceSearch widget or use another UI library, you can use connectors.

Connectors are higher-order components. They encapsulate the logic for a specific kind of widget and they provide a way to interact with the InstantSearch context.

They have an outer component API that we call exposed props, and they provide some other props to the wrapped components which are called the provided props.

It’s a 3-step process:

// 1. Create a React component
import { 
  connectSearchBox,
  createVoiceSearchHelper
} from 'react-instantsearch-dom';

class VoiceSearch extends React.Component {
  componentDidMount() {
    const { refine } = this.props;
    this.voiceSearchHelper = createVoiceSearchHelper({
      searchAsYouSpeak: false,
      onQueryChange: query => refine(query),
      onStateChange: () => {
        this.setState(this.voiceSearchHelper.getState());
      },
    });
    this.setState(this.voiceSearchHelper.getState());
  }

  componentWillUnmount() {
    if (this.voiceSearchHelper) {
      this.voiceSearchHelper.dispose();
    }
  }

  render() {
    if (!this.voiceSearchHelper) {
      return null;
    }

    const { status, transcript, isSpeechFinal, errorCode } = this.state;
    const { isBrowserSupported, isListening, toggleListening } =
      this.voiceSearchHelper;
    
    return (
      // return the DOM output
    );
  }
}

/*
this.voiceSearchHelper has the following properties:
- isBrowserSupported: () => boolean
   :`true` if user's browser supports voice search.
- isListening: () => boolean
   :`true` if listening to user's speech.
- toggleListening: () => void
   :Starts listening to user's speech, or stops it if already listening.
- getState: () => object
   :returns an object containing the following states regarding speech recognition:
     - `status: string`: current status (`initial`|`askingPermission`|
                         `waiting`|`recognizing`|`finished`|`error`).
     - `transcript: string`: currently recognized transcript.
     - `isSpeechFinal: boolean`: `true` if speech recognition is finished.
     - `errorCode: string|undefined`: an error code (if any).
              Refer to the spec for more information.
*/

// 2. Connect the component using the connector
// We re-use `connectSearchBox` here.
const CustomVoiceSearch = connectSearchBox(VoiceSearch);

// 3. Use your connected widget
<CustomVoiceSearch />

Create a React component

class VoiceSearch extends React.Component {
  ...
  render() {
    const {
      function refine,
    } = this.props;

    // return the DOM output
  }
}

Provided Props

refine
type: function

Changes the current query.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class VoiceSearch extends React.Component {
  componentDidMount() {
    const { refine } = this.props;
    this.voiceSearchHelper = createVoiceSearchHelper({
      searchAsYouSpeak: false,
      // refine with query passed from `voiceSearchHelper`.
      onQueryChange: query => refine(query),
      onStateChange: () => {
        this.setState(this.voiceSearchHelper.getState());
      },
    });
    this.setState(this.voiceSearchHelper.getState());
  }
  ...
}

Create and instantiate your connected widget

const CustomVoiceSearch = connectSearchBox(VoiceSearch);

<CustomVoiceSearch />

Full example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import {
  connectSearchBox,
  createVoiceSearchHelper
} from 'react-instantsearch-dom';

class VoiceSearch extends React.Component {
  componentDidMount() {
    const { refine } = this.props;
    this.voiceSearchHelper = createVoiceSearchHelper({
      searchAsYouSpeak: false,
      onQueryChange: query => refine(query),
      onStateChange: () => {
        this.setState(this.voiceSearchHelper.getState());
      },
    });
    this.setState(this.voiceSearchHelper.getState());
  }

  componentWillUnmount() {
    if (this.voiceSearchHelper) {
      this.voiceSearchHelper.dispose();
    }
  }

  render() {
    if (!this.voiceSearchHelper) {
      return null;
    }

    const { status, transcript, isSpeechFinal, errorCode } = this.state;
    const { isBrowserSupported, isListening, toggleListening } =
      this.voiceSearchHelper;
    
    return (
      <div>
        <button
          type="button"
          title="Voice Search"
          onClick={toggleListening}
          disabled={!isBrowserSupported()}
        >
          {isListening() ? 'Stop' : 'Start'}
        </button>
        <div>
          <p>status: {status}</p>
          <p>transcript: {transcript}</p>
          <p>isSpeechFinal: {isSpeechFinal ? 'true' : 'false'}</p>
          <p>errorCode: {errorCode}</p>
          <p>isListening: {isListening() ? 'true' : 'false'}</p>
          <p>isBrowserSupported: {isBrowserSupported() ? 'true' : 'false'}</p>
        </div>
      </div>
    );
  }
}
const CustomVoiceSearch = connectSearchBox(VoiceSearch);

HTML output

1
2
3
4
5
6
7
8
<div class="ais-VoiceSearch">
  <button class="ais-VoiceSearch-button" type="button" title="Search by voice">
    ...
  </button>
  <div class="ais-VoiceSearch-status">
    ...
  </div>
</div>

Did you find this page helpful?