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 & highlightProps props, available in both useSpeech hook and <Speech> component, can be used to highlight text and modify its styling.

Here, you may want to pass a JSX.Element instead of a plain string as text property for better presentation and ease of use, and react-text-to-speech supports it!

NOTE: It is recommended to memoize the JSX.Element first before passing it to text property for performance optimizations.

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,
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 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 "../components/dist";

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 following packages:

  • html-react-parser
  • react-markdown
  • remark-gfm (optional)
npm install html-react-parser react-markdown remark-gfm --save

Optionally, we also recommended to setup TailwindCSS and TailwindCSS Typography for styling purposes.

Custom MarkdownText Component
import parse from "html-react-parser";
import { useLayoutEffect, useMemo, useState } from "react";
import Markdown from "react-markdown";
import { useSpeech } from "react-text-to-speech";
import { HiMiniStop, HiVolumeOff, HiVolumeUp } from "react-text-to-speech/icons";
import remarkGfm from "remark-gfm";

function MarkdownText({ text }) {
const [showMarkdown, setShowMarkdown] = useState(true);
const [markdown, setMarkdown] = useState("");
const mdText = useMemo(() => <>{showMarkdown ? parse(markdown) : text}</>, [text, markdown]);
const { Text, speechStatus, start, pause, stop } = useSpeech({ text: mdText, highlightText: true });

useLayoutEffect(() => {
stop();
setMarkdown(document.querySelector(".rtts-markdown")?.innerHTML);
}, [text, showMarkdown]);

function toggleMarkdown() {
stop();
setTimeout(() => setShowMarkdown((prev) => !prev), 1);
}

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={toggleMarkdown}>
Toggle Markdown
</button>
<div className="flex space-x-4">
<button>{speechStatus !== "started" ? <HiVolumeUp title="Start speech" onClick={start} /> : <HiVolumeOff title="Pause speech" onClick={pause} />}</button>
<button>
<HiMiniStop title="Stop speech" onClick={stop} />
</button>
</div>
</div>
<div className="prose max-w-[90vw] overflow-x-scroll whitespace-pre-wrap break-words leading-snug *:my-0 *:w-max *:max-w-full prose-headings: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">
<Text />
</div>
{showMarkdown && (
<Markdown className="rtts-markdown hidden" remarkPlugins={[remarkGfm]}>
{text}
</Markdown>
)}
</div>
);
}

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

API Reference

Check the API Reference for more details.