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 totrue
, 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
- yarn
- pnpm
- bun
npm install react-remarkify rehype-raw rehype-sanitize remark-gfm --save
yarn add react-remarkify rehype-raw rehype-sanitize remark-gfm
pnpm add react-remarkify rehype-raw rehype-sanitize remark-gfm
bun add react-remarkify rehype-raw rehype-sanitize remark-gfm
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
.
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>
), includerehype-raw
andrehype-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 likeonerror
or tags like<script>
. - Optional Dependencies: If HTML support is not needed, you can skip installing
rehype-raw
andrehype-sanitize
.
API Reference
Check the API Reference for more details.