{"id":18563126,"url":"https://github.com/compulim/react-dictate-button","last_synced_at":"2025-04-09T16:20:43.858Z","repository":{"id":32635924,"uuid":"138520256","full_name":"compulim/react-dictate-button","owner":"compulim","description":"A button to start dictation using Web Speech API.","archived":false,"fork":false,"pushed_at":"2025-02-13T08:46:22.000Z","size":1897,"stargazers_count":26,"open_issues_count":7,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T10:47:00.070Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://compulim.github.io/react-dictate-button/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/compulim.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-06-24T22:38:53.000Z","updated_at":"2025-02-13T08:46:23.000Z","dependencies_parsed_at":"2024-06-18T20:01:10.933Z","dependency_job_id":"49f81c06-40f1-4880-9b0a-6e14b55d0770","html_url":"https://github.com/compulim/react-dictate-button","commit_stats":{"total_commits":64,"total_committers":2,"mean_commits":32.0,"dds":0.125,"last_synced_commit":"49ca7f88829a9c4c1351210a0bab0ffec354c0d0"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/compulim%2Freact-dictate-button","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/compulim%2Freact-dictate-button/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/compulim%2Freact-dictate-button/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/compulim%2Freact-dictate-button/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/compulim","download_url":"https://codeload.github.com/compulim/react-dictate-button/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247980817,"owners_count":21027803,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T22:11:52.485Z","updated_at":"2025-04-09T16:20:43.830Z","avatar_url":"https://github.com/compulim.png","language":"TypeScript","readme":"# react-dictate-button\n\n[![npm version](https://badge.fury.io/js/react-dictate-button.svg)](https://badge.fury.io/js/react-dictate-button) [![Build Status](https://travis-ci.org/compulim/react-dictate-button.svg?branch=master)](https://travis-ci.org/compulim/react-dictate-button)\n\nA button to start speech recognition using [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API), with an easy to understand event lifecycle.\n\n# Breaking changes\n\n## [2.0.0] - 2021-05-15\n\n- Requires [`react@\u003e=16.8.0`](https://npmjs.com/package/react) and [`core-js@3`](https://npmjs.com/package/core-js`)\n- Modifying props while recognition has started will no longer abort recognition immediately, props will be updated in next recognition\n- `SpeechGrammarList` is only constructed when `grammar` props is present\n- If `speechRecognition` prop is not present, capability detection is now done through `window.mediaDevices.getUserMedia`\n\n# Demo\n\nTry out this component at [github.io/compulim/react-dictate-button](https://github.io/compulim/react-dictate-button/).\n\n# Background\n\nReasons why we need to build our own component, instead of using [existing packages](https://www.npmjs.com/search?q=react%20speech) on NPM:\n\n- Most browsers required speech recognition (or WebRTC) to be triggered by a user event (button click)\n- Bring your own engine for [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API/Using_the_Web_Speech_API)\n  - Enable speech recognition on unsupported browsers by bridging it with [cloud-based service](https://npmjs.com/package/web-speech-cognitive-services)\n- Support grammar list thru [JSpeech Grammar Format](https://www.w3.org/TR/jsgf/)\n- Ability to interrupt recognition\n- Ability to [morph into other elements](#customization-thru-morphing)\n\n# How to use\n\nFirst, install our production version by `npm install react-dictate-button`. Or our development version by `npm install react-dictate-button@master`.\n\n```jsx\nimport { DictateButton } from 'react-dictate-button';\n\nexport default () =\u003e (\n  \u003cDictateButton\n    className=\"my-dictate-button\"\n    grammar=\"#JSGF V1.0; grammar districts; public \u003cdistrict\u003e = Tuen Mun | Yuen Long;\"\n    lang=\"en-US\"\n    onDictate={this.handleDictate}\n    onProgress={this.handleProgress}\n  \u003e\n    Start/stop\n  \u003c/DictateButton\u003e\n);\n```\n\n## Props\n\n| Name                | Type                     | Default                                         | Description                                                                                                                                                                                                                                                       |\n| ------------------- | ------------------------ | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `className`         | `string`                 | `undefined`                                     | Class name to apply to the button                                                                                                                                                                                                                                 |\n| `continuous`        | `boolean`                | `false`                                         | `true` to set Web Speech API to use continuous mode and should continue to recognize until stop, otherwise, `false`                                                                                                                                               |\n| `disabled`          | `boolean`                | `false`                                         | `true` to abort ongoing recognition and disable the button, otherwise, `false`                                                                                                                                                                                    |\n| `extra`             | `{ [key: string]: any }` | `{}`                                            | Additional properties to set to [`SpeechRecognition`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition) before `start`, useful when bringing your own [`SpeechRecognition`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition) |\n| `grammar`           | `string`                 | `undefined`                                     | Grammar list in [JSGF format](https://developer.mozilla.org/en-US/docs/Web/API/SpeechGrammarList/addFromString)                                                                                                                                                   |\n| `lang`              | `string`                 | `undefined`                                     | Language to recognize, for example, `'en-US'` or [`navigator.language`](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/language)                                                                                                              |\n| `speechGrammarList` | `any`                    | `window.SpeechGrammarList` (or vendor-prefixed) | Bring your own [`SpeechGrammarList`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechGrammarList)                                                                                                                                                          |\n| `speechRecognition` | `any`                    | `window.SpeechRecognition` (or vendor-prefixed) | Bring your own [`SpeechRecognition`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition)                                                                                                                                                          |\n\n\u003e Note: change of `extra`, `grammar`, `lang`, `speechGrammarList`, and `speechRecognition` will not take effect until next speech recognition is started.\n\n## Events\n\n\u003ctable\u003e\n  \u003cthead\u003e\n    \u003ctr\u003e\n      \u003cth\u003eName\u003c/th\u003e\n      \u003cth\u003eSignature\u003c/th\u003e\n      \u003cth\u003eDescription\u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\u003ccode\u003eonClick\u003c/code\u003e\u003c/th\u003e\n      \u003ctd\u003e\u003cpre\u003e(event: \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent\"\u003eMouseEvent\u003c/a\u003e) =\u003e void\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003eEmit when the user click on the button, \u003ccode\u003epreventDefault\u003c/code\u003e will stop recognition from starting\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\u003ccode\u003eonDictate\u003c/code\u003e\u003c/th\u003e\n      \u003ctd\u003e\n        \u003cpre\u003e({\n  result: {\n    confidence: number,\n    transcript: number\n  },\n  type: 'dictate'\n}) =\u003e void\u003c/pre\u003e\n      \u003c/td\u003e\n      \u003ctd\u003eEmit when recognition is completed\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\u003ccode\u003eonError\u003c/code\u003e\u003c/th\u003e\n      \u003ctd\u003e\u003cpre\u003e(event: \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionErrorEvent\"\u003eSpeechRecognitionErrorEvent\u003c/a\u003e) =\u003e void\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003eEmit when error has occurred or recognition is interrupted, \u003ca href=\"#event-lifecycle\"\u003esee below\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\u003ccode\u003eonProgress\u003c/code\u003e\u003c/th\u003e\n      \u003ctd\u003e\n        \u003cpre\u003e({\n  abortable: boolean,\n  results: [{\n    confidence: number,\n    transcript: number\n  }],\n  type: 'progress'\n}) =\u003e void\u003c/pre\u003e\n      \u003c/td\u003e\n      \u003ctd\u003eEmit for interim results, the array contains every segments of recognized text\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\u003ccode\u003eonRawEvent\u003c/code\u003e\u003c/th\u003e\n      \u003ctd\u003e\u003cpre\u003e(event: \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionEvent\"\u003eSpeechRecognitionEvent\u003c/a\u003e) =\u003e void\u003c/pre\u003e\u003c/td\u003e\n      \u003ctd\u003e\n        Emit for handling raw events from\n        \u003ca href=\"https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognitionEvent\"\n          \u003e\u003ccode\u003eSpeechRecognition\u003c/code\u003e\u003c/a\n        \u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n## Hooks\n\n\u003e Although previous versions exported a React Context, it is recommended to use the hooks interface.\n\n| Name            | Signature   | Description                                                                                         |\n| --------------- | ----------- | --------------------------------------------------------------------------------------------------- |\n| `useAbortable`  | `[boolean]` | If ongoing speech recognition has `abort()` function and can be aborted, `true`, otherwise, `false` |\n| `useReadyState` | `[number]`  | Returns the current state of recognition, refer to [this section](#function-as-a-child)             |\n| `useSupported`  | `[boolean]` | If speech recognition is supported, `true`, otherwise, `false`                                      |\n\n### Checks if speech recognition is supported\n\nTo determines whether speech recognition is supported in the browser:\n\n- If `speechRecognition` prop is `undefined`\n  - If both [`window.navigator.mediaDevices`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices) and [`window.navigator.mediaDevices.getUserMedia`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) are falsy, it is not supported\n    - Probably the browser is not on a secure HTTP connection\n  - If both `window.SpeechRecognition` and vendor-prefixed are falsy, it is not supported\n  - If recognition failed once with `not-allowed` error code, it is not supported\n- Otherwise, it is supported\n\n\u003e Even the browser is on an insecure HTTP connection, `window.SpeechRecognition` (or vendor-prefixed) will continue to be truthy. Instead, `mediaDevices.getUserMedia` is used for capability detection.\n\n### Event lifecycle\n\nOne of the design aspect is to make sure events are easy to understand and deterministic. First rule of thumb is to make sure `onProgress` will lead to either `onDictate` or `onError`. Here are some samples of event firing sequence (tested on Chrome 67):\n\n- Happy path: speech is recognized\n  1.  `onStart`\n  1.  `onProgress({})` (just started, therefore, no `results`)\n  1.  `onProgress({ results: [] })`\n  1.  `onDictate({ result: ... })`\n  1.  `onEnd`\n- Happy path: speech is recognized with continuous mode\n  1.  `onStart`\n  1.  `onProgress({})` (just started, therefore, no `results`)\n  1.  `onProgress({ results: [] })`\n  1.  `onDictate({ result: ... })`\n  1.  `onProgress({ results: [] })`\n  1.  `onDictate({ result: ... })`\n  1.  `onEnd`\n- Heard some sound, but nothing can be recognized\n  1.  `onStart`\n  1.  `onProgress({})`\n  1.  `onDictate({})` (nothing is recognized, therefore, no `result`)\n  1.  `onEnd`\n- Nothing is heard (audio device available but muted)\n  1.  `onStart`\n  1.  `onProgress({})`\n  1.  `onError({ error: 'no-speech' })`\n  1.  `onEnd`\n- Recognition aborted\n  1.  `onStart`\n  1.  `onProgress({})`\n  1.  `onProgress({ results: [] })`\n  1.  While speech is getting recognized, set `props.disabled` to `false`, abort recognition\n  1.  `onError({ error: 'aborted' })`\n  1.  `onEnd`\n- Not authorized to use speech or no audio device is availablabortable: truee\n  1.  `onStart`\n  1.  `onError({ error: 'not-allowed' })`\n  1.  `onEnd`\n\n## Function as a child\n\nInstead of passing child elements, you can pass a function to render different content based on ready state. This is called [function as a child](https://reactjs.org/docs/render-props.html#using-props-other-than-render).\n\n| Ready state | Description                                                                |\n| ----------- | -------------------------------------------------------------------------- |\n| `0`         | Not started                                                                |\n| `1`         | Starting recognition engine, recognition is not ready until it turn to `2` |\n| `2`         | Recognizing                                                                |\n| `3`         | Stopping                                                                   |\n\nFor example,\n\n```jsx\n\u003cDictateButton\u003e\n  {({ readyState }) =\u003e\n    readyState === 0 ? 'Start' : readyState === 1 ? 'Starting...' : readyState === 2 ? 'Listening...' : 'Stopping...'\n  }\n\u003c/DictateButton\u003e\n```\n\n# Customization thru morphing\n\nYou can build your own component by copying our layout code, without messing around the [logic code behind the scene](packages/component/src/Composer.js). For details, please refer to [`DictateButton.js`](packages/component/src/DictateButton.js), [`DictateCheckbox.js`](packages/component/src/DictateCheckbox.js), and [`DictationTextBox.js`](packages/pages/src/DictationTextBox.js).\n\n## Checkbox version\n\nIn addition to `\u003cbutton\u003e`, we also ship `\u003cinput type=\"checkbox\"\u003e` out of the box. The checkbox version is better suited for toggle button scenario and web accessibility. You can use the following code for the checkbox version.\n\n```jsx\nimport { DictateCheckbox } from 'react-dictate-button';\n\nexport default () =\u003e (\n  \u003cDictateCheckbox\n    className=\"my-dictate-checkbox\"\n    grammar=\"#JSGF V1.0; grammar districts; public \u003cdistrict\u003e = Redmond | Bellevue;\"\n    lang=\"en-US\"\n    onDictate={this.handleDictate}\n    onProgress={this.handleProgress}\n  \u003e\n    Start/stop\n  \u003c/DictateCheckbox\u003e\n);\n```\n\n## Text box with dictate button\n\nWe also provide a \"text box with dictate button\" version. But instead of shipping a full-fledged control, we make it a minimally-styled control so you can start copying the code and customize it in your own project. The sample code can be found at [DictationTextBox.js](packages/pages/src/DictationTextBox.js).\n\n# Design considerations\n\n- Hide the complexity of Web Speech events because we only want to focus on recognition experience\n  - Complexity in lifecycle events: `onstart`, `onaudiostart`, `onsoundstart`, `onspeechstart`\n  - `onresult` may not fire in some cases, `onnomatch` is not fired in Chrome\n  - To reduce complexity, we want to make sure event firing are either:\n    - Happy path: `onProgress`, then either `onDictate` or `onError`\n    - Otherwise: `onError`\n- \"Web Speech\" could means speech synthesis, which is out of scope for this package\n- \"Speech Recognition\" could means we will expose Web Speech API as-is, which we want to hide details and make it straightforward for recognition scenario\n\n# Roadmap\n\nPlease feel free to [file](https://github.com/compulim/react-dictate-button/issues) suggestions.\n\n- While `readyState` is 1 or 3 (transitioning), the underlying speech engine cannot be started/stopped until the state transition is complete\n  - Need rework on the state management\n- Instead of putting all logic inside [`Composer.js`](packages/component/src/Composer.js), how about\n  1.  Write an adapter to convert `SpeechRecognition` into another object with simpler event model and `readyState`\n  2.  Rewrite `Composer.js` to bridge the new `SimpleSpeechRecognition` model and React Context\n  3.  Expose `SimpleSpeechRecognition` so people not on React can still benefit from the simpler event model\n\n# Contributions\n\nLike us? [Star](https://github.com/compulim/react-dictate-button/stargazers) us.\n\nWant to make it better? [File](https://github.com/compulim/react-dictate-button/issues) us an issue.\n\nDon't like something you see? [Submit](https://github.com/compulim/react-dictate-button/pulls) a pull request.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcompulim%2Freact-dictate-button","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcompulim%2Freact-dictate-button","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcompulim%2Freact-dictate-button/lists"}