Skip to main content

useSpeech Hook

Basic Usage

import React from "react";
import { useSpeech } from "react-text-to-speech";

export default function App() {
const {
Text, // Component that returns the modified text property
speechStatus, // String that stores current speech status
isInQueue, // Boolean that stores whether a speech utterance is either being spoken or present in queue
start, // Function to start the speech or put it in queue
pause, // Function to pause the speech
stop, // Function to stop the speech or remove it from queue
} = useSpeech({ text: "This library is awesome!" });

return (
<div style={{ display: "flex", flexDirection: "column", rowGap: "1rem" }}>
<Text />
<div style={{ display: "flex", columnGap: "0.5rem" }}>
{speechStatus !== "started" ? <button onClick={start}>Start</button> : <button onClick={pause}>Pause</button>}
<button onClick={stop}>Stop</button>
</div>
</div>
);
}

Highlight Text

The highlightText, showOnlyHighlightedText, highlightMode, and highlightProps props, available in both the useSpeech hook and <Speech> component, can be used to highlight text and modify its styling during speech.

  • highlightText: Enables or disables text highlighting while the speech is active.
  • showOnlyHighlightedText: If set to true, only the currently highlighted text is returned (useful when you want to isolate the highlighted portion of text).
  • highlightMode: Controls the level of text highlighting, with options:
    • word: Highlights one word at a time.
    • sentence: Highlights text until the next sentence-ending punctuation (e.g., ., ?, !, or \n).
    • line: Highlights text until the next newline (\n).
    • paragraph: Highlights an entire paragraph or node.
  • highlightProps: Defines the styling for the highlighted text.

Here, you can enhance both presentation and flexibility by passing a ReactNode instead of a plain string to the text property, and react-text-to-speech fully supports this feature!

NOTE: It is recommended to memoize the ReactNode before passing it to the text property to improve performance.

import React, { useMemo, useState } from "react";
import { useSpeech } from "react-text-to-speech";

export default function App() {
const [highlightText, setHighlightText] = useState(true);
const text = useMemo(
() => (
<div>
<span>This library is awesome!</span>
<div>
<div>
<span>It can also read and highlight </span>
<span>nested text... </span>
<span>
<span>upto </span>
<span>
any
<span> level.</span>
</span>
</span>
</div>
</div>
</div>
),
[]
);
const { Text, speechStatus, start, pause, stop } = useSpeech({
text,
highlightText,
showOnlyHighlightedText: false,
highlightMode: "word",
highlightProps: { style: { color: "white", backgroundColor: "blue" } },
});

return (
<div style={{ display: "flex", flexDirection: "column", rowGap: "1rem" }}>
<Text />
<div style={{ display: "flex", columnGap: "0.5rem" }}>
{speechStatus !== "started" ? <button onClick={start}>Start</button> : <button onClick={pause}>Pause</button>}
<button onClick={stop}>Stop</button>
<button onClick={() => setHighlightText(!highlightText)}>Toggle Highlight Text</button>
</div>
</div>
);
}

Handling Errors and Events

import React from "react";
import { useSpeech } from "react-text-to-speech";

export default function App() {
const { Text, speechStatus, start, pause, stop } = useSpeech({
text: "This library can handle different errors and speech events!",
onError: (error) => {
alert(error.message);
},
onStart: (event) => {
console.log("Speech Started:", event);
},
onResume: (event) => {
console.log("Speech Resumed:", event);
},
onPause: (event) => {
console.log("Speech Paused:", event);
},
onStop: (event) => {
console.log("Speech Stopped:", event);
},
onBoundary: (event) => {
console.log("Boundary:", event);
},
onQueueChange: (queue) => {
console.log("Queue updated:", queue);
},
});

return (
<div style={{ display: "flex", flexDirection: "column", rowGap: "1rem" }}>
<Text />
<div style={{ display: "flex", columnGap: "0.5rem" }}>
{speechStatus !== "started" ? <button onClick={start}>Start</button> : <button onClick={pause}>Pause</button>}
<button onClick={stop}>Stop</button>
</div>
</div>
);
}

Multiple Instance Usage

The preserveUtteranceQueue prop, available in both useSpeech hook and <Speech> component, facilitates handling multiple speech instances conveniently.
If set to false, any currently speaking speech utterance will be stopped immediately upon initiating a new utterance. The new utterance will be spoken without waiting for the previous one to finish.
If set to true, new speech utterances will be added to a queue. They will be spoken once the previous speech instances are completed. This allows for sequential delivery of speech content, maintaining order and avoiding overlapping utterances.

The useQueue hook can be used to keep track of the queue as well as change the queue as shown below. The onQueueChange event handler used above can also be used to keep track of queue updates.

import React, { useMemo } from "react";
import { useQueue, useSpeech } from "react-text-to-speech";

function NewsItem({ title, desc }) {
const text = useMemo(
() => (
<>
<h4 style={{ margin: 0 }}>{title}</h4>
<div style={{ marginBottom: "0.5rem" }}>{desc}</div>
</>
),
[title, desc]
);
const { Text, speechStatus, isInQueue, start, stop } = useSpeech({ text, preserveUtteranceQueue: true });

return (
<div>
<Text />
<div style={{ display: "flex", columnGap: "0.5rem" }}>{!isInQueue ? <button onClick={start}>Start</button> : <button onClick={stop}>Stop</button>}</div>
</div>
);
}

export default function App() {
// 'news' holds response from some News API
const news = [
{ id: "1", title: "First random title.", desc: "First random description." },
{ id: "2", title: "Second random title.", desc: "Second random description." },
{ id: "3", title: "Third random title.", desc: "Third random description." },
];
const {
queue, // Queue that stores all the speech utterances
clearQueue, // Function to clear the queue
dequeue, // Function to remove a speech utterance from the queue at a specific index
} = useQueue();

return (
<div>
<div style={{ display: "flex", flexDirection: "column", rowGap: "1rem" }}>
{news.map(({ id, title, desc }) => (
<NewsItem key={id} title={title} desc={desc} />
))}
</div>
<div style={{ display: "flex", flexDirection: "column", rowGap: "1rem", marginBlock: "2rem" }}>
{queue.length ? (
queue.map(({ text }, i) => (
<div key={i}>
<button onClick={() => dequeue(i)}>Dequeue</button>
<span style={{ marginLeft: "1rem" }}>{text}</span>
</div>
))
) : (
<div>Queue is Empty</div>
)}
</div>
<button onClick={clearQueue}>Clear Queue</button>
</div>
);
}

Usage with Markdown

To use react-text-to-speech seamlessly with markdown text, it is recommended to install the following packages:

  • react-remarkify
  • rehype-raw (optional, for HTML support)
  • rehype-sanitize (optional, for secure HTML support)
  • remark-gfm (optional)

Installation

npm install react-remarkify rehype-raw rehype-sanitize remark-gfm --save

Optionally, we also recommend setting up TailwindCSS and TailwindCSS Typography for styling purposes.

Custom MarkdownText Component

The following example demonstrates a custom MarkdownText component supporting HTML parsing using rehype-raw and sanitization using rehype-sanitize. If HTML support is not required, you can skip installing rehype-raw and rehype-sanitize.

Custom MarkdownText Component
import { useState } from "react";
import { useRemark } from "react-remarkify";
import { useSpeech } from "react-text-to-speech";
import { HiMiniStop, HiVolumeOff, HiVolumeUp } from "react-text-to-speech/icons";
import rehypeRaw from "rehype-raw";
import rehypeSanitize from "rehype-sanitize";
import remarkGfm from "remark-gfm";

function MarkdownText({ text }) {
const [showMarkdown, setShowMarkdown] = useState(true);
const reactContent = useRemark({
markdown: text,
rehypePlugins: [rehypeRaw, rehypeSanitize],
remarkPlugins: [remarkGfm],
remarkToRehypeOptions: { allowDangerousHtml: true },
});
const { Text, speechStatus, start, pause, stop } = useSpeech({ text: showMarkdown ? reactContent : text, highlightText: true });

return (
<div className="flex flex-col space-y-3 p-4 text-justify">
<div className="flex w-fit flex-col items-center space-y-2">
<button className="rounded-sm border-2 border-black bg-gray-100 px-1 py-0.5 text-sm" onClick={() => setShowMarkdown((prev) => !prev)}>
Toggle Markdown
</button>
<div className="flex space-x-4">
<span role="button">{speechStatus !== "started" ? <HiVolumeUp title="Start speech" onClick={start} /> : <HiVolumeOff title="Pause speech" onClick={pause} />}</span>
<span role="button">
<HiMiniStop title="Stop speech" onClick={stop} />
</span>
</div>
</div>
<div className="prose prose-headings:my-1 prose-pre:my-1 prose-pre:w-full prose-li:my-0 prose-table:w-full prose-table:table-fixed prose-th:border prose-th:p-2 prose-td:border prose-td:p-2 grid max-w-full grid-cols-1 overflow-y-scroll whitespace-pre-wrap break-words *:my-0 *:w-max *:max-w-full *:whitespace-pre-wrap">
<Text />
</div>
</div>
);
}

export default function App() {
return <MarkdownText text={`# react-text-to-speech\nThis library is awesome`} />;
}

Notes

  • HTML Support: If you want to render HTML elements in markdown (e.g., <table> or inline <strong>), include rehype-raw and rehype-sanitize.
  • Security: To ensure safe rendering of HTML content, especially if the input comes from untrusted sources, always use rehype-sanitize to filter dangerous attributes like onerror or tags like <script>.
  • Optional Dependencies: If HTML support is not needed, you can skip installing rehype-raw and rehype-sanitize.

API Reference

Check the API Reference for more details.