How to Add Voice to Your React App with ElevenLabs
Why Add Voice to a React App? Voice is the next frontier of UI. Whether you’re building a language‑learning tool, an accessibility‑first dashboard, or a quirky chatbot, a natural‑sounding voice can make the experience feel human . Text‑to‑speech (TTS) services have come a long way, and today you ca

Voice is the next frontier of UI. Whether you’re building a language‑learning tool, an accessibility‑first dashboard, or a quirky chatbot, a natural‑sounding voice can make the experience feel human. Text‑to‑speech (TTS) services have come a long way, and today you can get studio‑grade quality with just a few lines of code.
One of the best options on the market right now is ElevenLabs – a modern TTS platform that offers ultra‑realistic speech, custom voice cloning, and a straightforward REST API. In this guide we’ll walk through adding ElevenLabs voice output to a React app, from getting an API key to playing back audio on the fly.
TL;DR – By the end of this article you’ll have a reusable useElevenLabs hook that turns any string into speech, and you’ll be ready to experiment with custom voices.
Pro tip: Store the key in a.env file and never commit it to version control.
#.env REACT_APP_ELEVENLABS_API_KEY=your_secret_key_here Enter fullscreen mode Exit fullscreen mode 2. Set Up a Fresh React Project If you already have a project, skip to the next step. Otherwise:
npx create-react-app voice-demo cd voice-demo npm install axios Enter fullscreen mode Exit fullscreen mode We’ll use axios for HTTP calls, but fetch works just as well.
Create a new file src/api/elevenlabs.js:
// src/api/elevenlabs.js import axios from " axios "; const BASE_URL = " https://api.elevenlabs.io/v1 "; export const synthesizeSpeech = async ({ text, voiceId = " EXAVITQu4vr4xnSDxMaL ", // default “Rachel” voice stability = 0.75, similarityBoost = 0.75, }) => { const apiKey = process. env. REACT_APP_ELEVENLABS_API_KEY; if (! apiKey ) throw new Error ( " ElevenLabs API key not set " ); const response = await axios ({ method: " POST ", url: ` ${ BASE_URL } /text-to-speech/ ${ voiceId } `, headers: { " xi-api-key ": apiKey, " Content-Type ": " application/json ", Accept: " audio/mpeg ", }, data: { text, voice_settings: { stability, similarity_boost: similarityBoost }, }, responseType: " blob ", // we want raw audio }); return URL. createObjectURL ( response. data ); }; Enter fullscreen mode Exit fullscreen mode What’s happening?
Now we’ll wrap the service in a React hook that handles loading state and errors.
// src/hooks/useElevenLabs.js import { useState, useCallback } from " react "; import { synthesizeSpeech } from "../api/elevenlabs "; export const useElevenLabs = () => { const [ audioUrl, setAudioUrl ] = useState ( null ); const [ loading, setLoading ] = useState ( false ); const [ error, setError ] = useState ( null ); const speak = useCallback ( async ( text ) => { setLoading ( true ); setError ( null ); try { const url = await synthesizeSpeech ({ text }); setAudioUrl ( url ); } catch ( e ) { console. error ( e ); setError ( e ); } finally { setLoading ( false ); } }, [] ); const reset = () => { setAudioUrl ( null ); setError ( null ); }; return { speak, audioUrl, loading, error, reset }; }; Enter fullscreen mode Exit fullscreen mode The hook returns:
Let’s create a component that lets users type a sentence and hear it back.
// src/components/VoiceBox.jsx import { useState } from " react "; import { useElevenLabs } from "../hooks/useElevenLabs "; export const VoiceBox = () => { const [ input, setInput ] = useState ( "" ); const { speak, audioUrl, loading, error, reset } = useElevenLabs (); const handleSpeak = async () => { if (! input. trim ()) return; await speak ( input ); }; return ( < div style = { { maxWidth: 600, margin: " 2rem auto ", textAlign: " center " } } > < h2 > 🔊 Speak Anything </ h2 > < textarea rows = { 3 } placeholder = "Type something..." value = { input } onChange = { ( e ) => setInput ( e. target. value ) } style = { { width: " 100% ", padding: " 0.5rem " } } /> < br /> < button onClick = { handleSpeak } disabled = { loading } style = { { marginTop: " 1rem " } } > { loading? " Generating… ": " Speak " } </ button > { error && < p style = { { color: " red " } } > ❗️ { error. message } </ p > } { audioUrl && ( < div style = { { marginTop: " 1rem " } } > < audio src = { audioUrl } controls autoPlay onEnded = { reset } /> </ div > ) } </ div > ); }; Enter fullscreen mode Exit fullscreen mode Add the component to App.js:
import { VoiceBox } from "./components/VoiceBox "; function App () { return ( < div className = "App" > < VoiceBox /> </ div > ); } export default App; Enter fullscreen mode Exit fullscreen mode Run npm start and you should be able to type text, click Speak, and hear a natural‑sounding voice in seconds.
ElevenLabs also lets you create a custom voice from a short audio sample (as little as 30 seconds). The workflow is:
curl -X POST "https://api.elevenlabs.io/v1/voices/add" \ -H "xi-api-key: $ELEVENLABS_API_KEY " \ -F "name=MyCustomVoice" \ -F "files=@/path/to/voice_sample.wav" Enter fullscreen mode Exit fullscreen mode The response contains:
{ "voice_id": "abcd1234efgh5678ijkl", "name": "MyCustomVoice", "preview_url": "https://..." } Enter fullscreen mode Exit fullscreen mode Plug that voice_id into our wrapper:
await synthesizeSpeech ({ text: " Hello world! ", voiceId: " abcd1234efgh5678ijkl " }); Enter fullscreen mode Exit fullscreen mode Now the app speaks your voice. This opens doors for personalized assistants, brand‑specific narrations, or even recreating historic figures (within legal bounds, of course).
Head over to ElevenLabs, grab your free API key, and start experimenting: https://try.elevenlabs.io/kr07zfuqn1bp. Happy coding, and may your apps be ever more talkative!