ElevenLabs + React: Building a Voice-Enabled UI
Why Voice‑Enabled UI Matters Every day we interact with voice assistants, chatbots, and smart devices. Even if you’re a web developer who’s never written a line of code for speech, adding a voice layer to your React ap
Why Voice‑Enabled UI Matters
Every day we interact with voice assistants, chatbots, and smart devices. Even if you’re a web developer who’s never written a line of code for speech, adding a voice layer to your React app can dramatically improve accessibility, engagement, and user flow. Imagine a customer support portal where users can simply ask a question instead of typing it, or a dashboard where you can “listen” to metrics while multitasking. Voice isn’t just a novelty—it’s a first‑class UI element that can make your product feel more human.
The Challenge: Text‑to‑Speech on the Web
Web browsers expose the SpeechSynthesis API, but it’s limited to a handful of built‑in voices that feel generic. If you want natural, expressive speech with custom voice cloning, you need an external service that offers high‑quality neural TTS. That’s where ElevenLabs comes in. Their API delivers realistic, expressive voices and lets you clone your own voice or a character’s voice in minutes.
Getting Started with ElevenLabs
Sign Up
Head over to the ElevenLabs sign‑up page and create an account. When you’re ready to start, use this referral link to get a free trial:
https://try.elevenlabs.io/kr07zfuqn1bpAPI Key
Once you’re logged in, navigate to API Settings and copy your key. Keep it secret—store it in environment variables or a secure secrets manager.Choose a Voice
ElevenLabs offers a catalog of voices. Pick one that fits your brand tone. If you’re interested in voice cloning, you can upload a few minutes of audio and generate a custom voice model in under an hour.
Building the React Component
Below is a minimal example that demonstrates how to send text to ElevenLabs, receive an MP3 stream, and play it back in a React component.
import { useState, useRef } from "react";
export default function VoiceChat() {
const [text, setText] = useState("");
const [loading, setLoading] = useState(false);
const audioRef = useRef(null);
const handleSpeak = async () => {
setLoading(true);
const response = await fetch("/api/tts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text })
});
if (!response.ok) throw new Error("TTS request failed");
const blob = await response.blob();
const url = URL.createObjectURL(blob);
audioRef.current.src = url;
audioRef.current.play();
setLoading(false);
};
return (
<div style={{ maxWidth: 600, margin: "auto" }}>
<h2>Voice‑Enabled UI Demo</h2>
<textarea
rows="4"
style={{ width: "100%" }}
placeholder="Type something to speak"
value={text}
onChange={(e) => setText(e.target.value)}
/>
<button onClick={handleSpeak} disabled={loading || !text.trim()}>
{loading ? "Loading…" : "Speak"}
</button>
<audio ref={audioRef} controls style={{ width: "100%", marginTop: 20 }} />
</div>
);
}
What’s happening under the hood?
- The component posts the user’s text to a backend endpoint (
/api/tts). - The backend forwards the request to ElevenLabs’
/v1/text-to-speechendpoint. - ElevenLabs returns an MP3 blob which we play in an
<audio>element.
Backend Proxy (Node.js / Express)
You’ll need a small server to keep your API key hidden from the browser.
// server.js
const express = require("express");
const fetch = require("node-fetch");
const cors = require("cors");
require("dotenv").config();
const app = express();
app.use(cors());
app.use(express.json());
const ELEVEN_API_KEY = process.env.ELEVEN_API_KEY;
const ELEVEN_VOICE_ID = "YOUR_VOICE_ID"; // from the ElevenLabs console
app.post("/api/tts", async (req, res) => {
const { text } = req.body;
if (!text) return res.status(400).json({ error: "Missing text" });
try {
const response = await fetch(
`https://api.elevenlabs.io/v1/text-to-speech/${ELEVEN_VOICE_ID}`,
{
method: "POST",
headers: {
"xi-api-key": ELEVEN_API_KEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
text,
voice_settings: { stability: 0.5, similarity_boost: 0.75 }
})
}
);
if (!response.ok) {
const error = await response.text();
return res.status(500).json({ error, detail: error });
}
const audioBuffer = await response.buffer();
res.set({
"Content-Type": "audio/mpeg",
"Content-Disposition": "attachment; filename=output.mp3"
});
res.send(audioBuffer);
} catch (err) {
console.error(err);
res.status(500).json({ error: "Server error" });
}
});
app.listen(3001, () => console.log("TTS server listening on port 3001"));
Tip: Store your API key in a .env file:
ELEVEN_API_KEY=sk-XXXXXXXXXXXXXXXXXXXX
ELEVEN_VOICE_ID=1234567890abcdef
Voice Cloning in a Few Minutes
If you want a personalized voice for your app, ElevenLabs lets you clone a voice in as little as 30 minutes:
- Record 3–5 minutes of clear speech.
- Upload the file via the web console or using the
audioendpoint. - Wait for the model to be ready (usually < 30 min).
- Use the generated
voice_idin your TTS calls.
This is perfect for brand consistency or creating a custom avatar voice.
Handling Edge Cases
- Large Text Blocks – ElevenLabs has a 200‑character limit per request. Split longer passages into sentences and stream them sequentially.
- Latency – TTS requests can take 300–500 ms. Use a loading indicator or pre‑fetch frequently used phrases.
- Caching – Store generated MP3s in a CDN or local storage to avoid repeated API calls for the same text.
Integrating with Existing State Management
If you’re using Redux or Context API, you can dispatch a SPEAK_TEXT action that triggers the TTS flow. That way, any component can trigger speech without knowing the details of the backend.
// actions.js
export const speakText = (text) => async (dispatch) => {
dispatch({ type: "SPEAK_REQUEST" });
try {
const res = await fetch("/api/tts", {
method: "POST",
body: JSON.stringify({ text })
});
const blob = await res.blob();
dispatch({ type: "SPEAK_SUCCESS", payload: URL.createObjectURL(blob) });
} catch (e) {
dispatch({ type: "SPEAK_FAILURE", error: e.message });
}
};
Going Beyond: Real‑Time Speech Recognition
While ElevenLabs focuses on TTS, you can pair it with a speech‑to‑text service like AssemblyAI or Google Cloud Speech for a full duplex voice UI. Capture user speech, transcribe it, then send the resulting text back through ElevenLabs to get a spoken response.
Performance & SEO
Because TTS runs client‑side, the generated audio isn’t indexed by search engines. If you need pre‑rendered audio for SEO (e.g., podcasts), consider generating audio on the server and serving it as a static asset.
Accessibility Considerations
- Captioning – Provide a text transcript for each spoken segment.
-
Keyboard Shortcuts – Allow users to trigger speech with
Ctrl+Sor a custom hotkey. - Audio Controls – Always expose play/pause/seek controls; don’t rely solely on auto‑play.
Wrap‑Up
Adding voice to a React app is surprisingly straightforward when you have a robust API like ElevenLabs. The key steps:
- Get the API key – use the referral link to start for free: https://try.elevenlabs.io/kr07zfuqn1bp
- Create a small backend proxy – keep your key safe.
- Build a reusable React component – send text, receive audio, play.
- Optional: Clone a custom voice – for brand consistency or personalization.
- Handle edge cases – split long texts, cache results, provide UI feedback.
Voice isn’t a future trend; it’s a present‑day UX upgrade that can open your app to new audiences and use cases. Give it a try and see how your users respond.
Ready to bring your app to life with realistic voice? Sign up for ElevenLabs through this link, grab your API key, and start building: https://try.elevenlabs.io/kr07zfuqn1bp
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.