npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

tts-react

v4.0.0

Published

React hook and component for converting text to speech using the Web Speech API or Amazon Polly.

Downloads

1,070

Readme

tts-react

CI codecov NPM version

tts-react provides a hook (useTts) and component (TextToSpeech) to convert text to speech. In most cases you want the hook so you can use custom styling on the audio controls.

By default tts-react uses the SpeechSynthesis and SpeechSynthesisUtterance API's. You can fallback to the HTMLAudioElement API by providing a fetchAudioData prop to the hook or component.

Install

npm i react react-dom tts-react

For projects using React 18:

npm i react@18 react-dom@18 tts-react@3

Demo (Storybook)

morganney.github.io/tts-react

Example

ESM + CDN

Get up and running quickly using tts-react with ESM from a CDN. This example uses React 19 with htm:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>ESM + CDN + htm</title>
  </head>
  <body>
    <script type="module">
      import { createElement } from 'https://esm.sh/react'
      import { createRoot } from 'https://esm.sh/react-dom/client'
      import { TextToSpeech } from 'https://esm.sh/tts-react'
      import htm from 'https://esm.sh/htm'

      const html = htm.bind(createElement)

      createRoot(document.body).render(
        html`
          <${TextToSpeech} markTextAsSpoken>
            <p>Hello from tts-react.</p>
          </${TextToSpeech}>
        `
      )
    </script>
  </body>
</html>

Hook

You can use the hook to create a Speak component that converts the text to speech on render:

import { useTts } from 'tts-react'
import type { TTSHookProps } from 'tts-react'

type SpeakProps = Pick<TTSHookProps, 'children'>

const Speak = ({ children }: SpeakProps) => (
  <>{useTts({ children, autoPlay: true }).ttsChildren}</>
)

const App = () => {
  return (
    <Speak>
      <p>This text will be spoken on render.</p>
    </Speak>
  )
}

Or create a more advanced component with controls for adjusting the speaking:

import { useTts } from 'tts-react'
import type { TTSHookProps } from 'tts-react'

interface CustomProps extends TTSHookProps {
  highlight?: boolean
}

const CustomTTSComponent = ({ children, highlight = false }: CustomProps) => {
  const { ttsChildren, state, play, stop, pause } = useTts({
    children,
    markTextAsSpoken: highlight
  })

  return (
    <div>
      <>
        <button disabled={state.isPlaying} onClick={play}>
          Play
        </button>
        <button disabled={!state.isPlaying} onClick={pause}>
          Pause
        </button>
        <button onClick={stop}>Stop</button>
      </>
      {ttsChildren}
    </div>
  )
}

const App = () => {
  return (
    <CustomTTSComponent highlight>
      <p>Some text to be spoken and highlighted.</p>
    </CustomTTSComponent>
  )
}

Component

Use the TextToSpeech component for an out of the box solution:

import { TextToSpeech, Positions, Sizes } from 'tts-react'

const App = () => {
  return (
    <TextToSpeech
      markTextAsSpoken
      align="vertical"
      size={Sizes.SMALL}
      position={Positions.TL}>
      <p>Some text to be spoken.</p>
    </TextToSpeech>
  )
}

useTts

The hook returns the internal state of the audio being spoken, getters/setters of audio attributes, callbacks that can be used to control playing/stopping/pausing/etc. of the audio, and modified children if using markTextAsSpoken. The parameters accepted are described in the Props section. The response object is described by the TTSHookResponse type.

const {
  get,
  set,
  state,
  spokenText,
  ttsChildren,
  play,
  stop,
  pause,
  replay,
  playOrPause,
  playOrStop,
  toggleMute
} = useTts({
  lang,
  voice,
  children,
  autoPlay,
  markTextAsSpoken,
  markColor,
  markBackgroundColor,
  onStart,
  onBoundary,
  onPause,
  onEnd,
  onError,
  onVolumeChange,
  onPitchChange,
  onRateChange,
  fetchAudioData
})

interface TTSHookProps extends MarkStyles {
  /** The spoken text is extracted from here. */
  children: ReactNode
  /** The `SpeechSynthesisUtterance.lang` to use. */
  lang?: string
  /** The `SpeechSynthesisUtterance.voice` to use. */
  voice?: SpeechSynthesisVoice
  /** The initial rate of the speaking audio. */
  rate?: number
  /** The initial volume of the speaking audio. */
  volume?: number
  /** Whether the text should be spoken automatically, i.e. on render. */
  autoPlay?: boolean
  /** Whether the spoken word should be wrapped in a `<mark>` element. */
  markTextAsSpoken?: boolean
  /** Callback when the volume is changed.  */
  onVolumeChange?: (newVolume: number) => void
  /** Callback when the rate is changed.  */
  onRateChange?: (newRate: number) => void
  /** Callback when the pitch is changed.  */
  onPitchChange?: (newPitch: number) => void
  /** Callback when there is an error of any kind. */
  onError?: (msg: string) => void
  /** Callback when speaking/audio starts playing. */
  onStart?: (evt: SpeechSynthesisEvent | Event) => void
  /** Callback when the speaking/audio is paused. */
  onPause?: (evt: SpeechSynthesisEvent | Event) => void
  /** Calback when the current utterance/audio has ended. */
  onEnd?: (evt: SpeechSynthesisEvent | Event) => void
  /** Callback when a word boundary/mark has been reached. */
  onBoundary?: (evt: SpeechSynthesisEvent | Event) => void
  /** Function to fetch audio and speech marks for the spoken text. */
  fetchAudioData?: (spokenText: string) => Promise<TTSAudioData>
}
interface TTSHookResponse {
  set: {
    lang: (value: string) => void
    rate: (value: number) => void
    pitch: (value: number) => void
    volume: (value: number) => void
    preservesPitch: (value: boolean) => void
  }
  get: {
    lang: () => string
    rate: () => number
    pitch: () => number
    volume: () => number
    preservesPitch: () => boolean
  }
  /** State of the current speaking/audio. */
  state: TTSHookState
  /** The text extracted from the children elements and used to synthesize speech. */
  spokenText: string
  play: () => void
  stop: () => void
  pause: () => void
  replay: () => void
  /** Toggles between muted/unmuted, i.e. volume is zero or non-zero. */
  toggleMute: (callback?: (wasMuted: boolean) => void) => void
  /** Toggles between play/stop. */
  playOrStop: () => void
  /** Toggles between play/pause. */
  playOrPause: () => void
  /** The original children with a possible <mark> included if using `markTextAsSpoken`. */
  ttsChildren: ReactNode
}
interface TTSHookState {
  voices: SpeechSynthesisVoice[]
  boundary: BoundaryUpdate
  isPlaying: boolean
  isPaused: boolean
  isMuted: boolean
  isError: boolean
  isReady: boolean
}
interface TTSBoundaryUpdate {
  word: string
  startChar: number
  endChar: number
}

fetchAudioData

Using fetchAudioData will bypass SpeechSynthesis and use the HTMLAudioElement.

;(spokenText: string) => Promise<TTSAudioData>

When using fetchAudioData it must return TTSAudioData which has the following shape:

interface PollySpeechMark {
  end: number
  start: number
  time: number
  type: 'word'
  value: string
}
interface TTSAudioData {
  audio: string
  marks?: PollySpeechMark[]
}

The audio property must be a URL that can be applied to HTMLAudioElement.src, including a data URL. If using markTextAsSpoken then you must also return the marks that describe the word boundaries. PollySpeechMarks have the same shape as the Speech Marks used by Amazon Polly, with the restriction that they must be of type: 'word'.

Props

Most of these are supported by the useTts hook, but those marked with an asterisk are exclusive to the TextToSpeech component.

* Only applies to TextToSpeech component.

| Name | Required | Type | Default | Description | | ------------------------------ | -------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | children | yes | ReactNode | none | Provides the text that will be spoken. | | lang | no | string | The one used by SpeechSynthesisUtterance.lang. | Sets the SpeechSynthesisUtterance.lang. Overrides voice when set and voice.lang does not match lang. | | voice | no | SpeechSynthesisVoice | None or the voice provided by audio from TTSAudioData. | The voice heard when the text is spoken. Calling set.lang may override this value. | | autoPlay | no | boolean | false | Whether the audio of the text should automatically be spoken when ready. | | markTextAsSpoken | no | boolean | false | Whether the word being spoken should be highlighted. | | markColor | no | string | none | Color of the text that is currently being spoken. Only applies with markTextAsSpoken. | | markBackgroundColor | no | string | none | Background color of the text that is currently being spoken. Only applies with markTextAsSpoken. | | fetchAudioData | no | (text: string) => Promise<TTSAudioData> | none | Function to return the optional SpeechMarks[] and audio URL for the text to be spoken. See fetchAudioData for more details. | | *allowMuting | no | boolean | true | Whether an additional button will be shown on the component that allows muting the audio. | | *onMuteToggled | no | (wasMuted: boolean) => void | none | Callback when the user clicks the mute button shown from allowMuting being enabled. Can be used to toggle global or local state like whether autoPlay should be enabled. | | onStart | no | (evt: SpeechSynthesisEvent \| Event) => void | none | Callback when the speaking/audio has started (or resumed) playing. | | onPause | no | (evt: SpeechSynthesisEvent \| Event) => void | none | Callback when the speaking/audio has been paused. | | onEnd | no | (evt: SpeechSynthesisEvent \| Event) => void | none | Callback when the speaking/audio has stopped. | | onBoundary | no | (boundary: TTSBoundaryUpdate, evt: SpeechSynthesisEvent \| Event) => void | none | Callback when a word boundary/mark has been reached. | | onError | no | (msg: string) => void | none | Callback when there is an error of any kind playing the spoken text. The error message (if any) will be provided. | | onVolumeChange | no | (newVolume: number) => void | none | Callback when the volume has changed. | | onRateChange | no | (newRate: number) => void | none | Callback when the rate has changed. | | onPitchChange | no | (newPitch: number) => void | none | Callback when the pitch has changed. | | *align | no | 'horizontal' \| 'vertical' | 'horizontal' | How to align the controls within the TextToSpeech component. | | *size | no | 'small' \| 'medium' \| 'large' | 'medium' | The relative size of the controls within the TextToSpeech component. | | *position | no | 'topRight' \| 'topLeft' \| 'bottomRight' \| 'bottomLeft' | 'topRight' | The relative positioning of the controls within the TextToSpeech component. | | *useStopOverPause | no | boolean | false | Whether the controls should display a stop button instead of a pause button. On Android devices, SpeechSynthesis.pause() behaves like cancel(), so you can use this prop in that context. |

FAQ