Dev.to WebDev 🛠 Dev 👁 0 📖 5 min read

ElevenLabs + React: Building a Voice-Enabled UI

Introduction Building a voice‑enabled interface has moved from a niche research area to a mainstream UX strategy. Whether you’re adding a virtual assistant to a dashboard, creating an interactive e‑learning platform, o

Introduction

Building a voice‑enabled interface has moved from a niche research area to a mainstream UX strategy. Whether you’re adding a virtual assistant to a dashboard, creating an interactive e‑learning platform, or simply experimenting with speech‑to‑text and text‑to‑speech in your next project, the key is a reliable, high‑quality voice engine. In this post we’ll walk through how to wire up ElevenLabs (the go‑to text‑to‑speech provider) with a React front‑end, cover voice cloning basics, and give you a working code‑base you can fork right away.

Why Voice in Your UI?

  1. Accessibility – Text‑to‑speech (TTS) opens your app to users with visual impairments or reading difficulties.
  2. Multitasking – Users can listen while they’re on the go, freeing up screen real estate for other tasks.
  3. Engagement – A human‑like voice can make static content feel dynamic, improving retention and brand recall.
  4. Future‑proofing – Voice assistants (Alexa, Google Assistant, Siri) dominate the interaction layer. Integrating TTS early means you’re ready for a seamless handoff to those ecosystems.

Choosing a Voice Engine

When I started experimenting in 2023, I compared a handful of providers: Google Cloud TTS, Amazon Polly, Microsoft Azure Speech, and ElevenLabs. While the first three are battle‑tested and have generous free tiers, ElevenLabs consistently outperformed them on naturalness, speed, and ease of integration. Their API is intentionally lightweight, and the voices feel almost human‑like, which is perfect for UI demos and production use cases alike.

If you’re curious, give ElevenLabs a try at https://try.elevenlabs.io/kr07zfuqn1bp.

Setting Up the Project

We’ll scaffold a minimal React app using Vite (fast, modern build tool) and TypeScript for type safety. Feel free to drop the ts flag if you prefer plain JS.

npm create vite@latest eleven-react-voice -- --template react-ts
cd eleven-react-voice
npm install

Next, install a few helpers:

npm install axios @mui/material @emotion/react @emotion/styled
  • axios – HTTP client for calling the ElevenLabs API.
  • @mui/material – a lightweight UI library to keep our demo clean.

Getting an API Key

  1. Sign up at ElevenLabs.
  2. Navigate to the API Keys section.
  3. Copy your key.

We’ll store it in a .env file:

VITE_ELEVEN_KEY=sk_XXXXXXXXXXXXXXXXXXXXXXXX

Vite automatically exposes variables prefixed with VITE_ to the client. Never commit your key to version control! In production you’d route calls through a backend proxy.

Building the Voice Component

Below is a small React component that:

  1. Accepts text input.
  2. Sends it to ElevenLabs for synthesis.
  3. Streams back the audio blob and plays it.
  4. Shows a simple loading indicator.
// src/components/VoicePlayer.tsx
import { useState } from 'react';
import axios from 'axios';

const VoicePlayer = () => {
  const [text, setText] = useState('');
  const [loading, setLoading] = useState(false);
  const [audioSrc, setAudioSrc] = useState<string | null>(null);

  const synthesize = async () => {
    if (!text.trim()) return;
    setLoading(true);
    setAudioSrc(null);

    try {
      const res = await axios.post(
        'https://api.elevenlabs.io/v1/text-to-speech/your-voice-id',
        { text },
        {
          headers: {
            'Content-Type': 'application/json',
            Accept: 'audio/mpeg',
            Authorization: `Bearer ${import.meta.env.VITE_ELEVEN_KEY}`,
          },
          responseType: 'blob',
        }
      );

      const blob = new Blob([res.data], { type: 'audio/mpeg' });
      setAudioSrc(URL.createObjectURL(blob));
    } catch (err) {
      console.error('Synthesis failed', err);
      alert('Could not synthesize text. Check console for details.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <textarea
        rows={4}
        placeholder="Type something to speak..."
        value={text}
        onChange={e => setText(e.target.value)}
        style={{ width: '100%', marginBottom: 8 }}
      />
      <button onClick={synthesize} disabled={loading} style={{ width: '100%' }}>
        {loading ? 'Speaking…' : 'Speak'}
      </button>
      {audioSrc && (
        <audio controls src={audioSrc} style={{ width: '100%', marginTop: 12 }} />
      )}
    </div>
  );
};

export default VoicePlayer;

Tip – Replace your-voice-id with the ID of the voice you want to use. You can list available voices via the ElevenLabs API or the web dashboard.

Integrating Voice Cloning

ElevenLabs also offers voice cloning: you provide a short sample of a target voice (≈ 30 seconds), and the service generates a new voice model that you can use just like any other voice ID.

# Upload a sample recording (wav or mp3)
curl -X POST "https://api.elevenlabs.io/v1/voice-cloning" \
  -H "Authorization: Bearer $ELEVEN_KEY" \
  -H "Content-Type: multipart/form-data" \
  -F "audio_file=@/path/to/your_sample.wav" \
  -F "name=My Clone"

The response will contain a voice_id. Plug that into the component above, and you’ll hear your own voice.

Security note – Voice cloning data is highly personal. Treat the resulting voice ID like a private key.

Adding Contextual Prompting

Sometimes you want the assistant to respond to a user query. Combine ElevenLabs with OpenAI’s GPT‑4 or other LLMs to generate dynamic prompts, then feed the result into the TTS component.

// src/components/ChatWithVoice.tsx
import { useState } from 'react';
import axios from 'axios';
import VoicePlayer from './VoicePlayer';

const ChatWithVoice = () => {
  const [prompt, setPrompt] = useState('');
  const [response, setResponse] = useState('');

  const handleChat = async () => {
    if (!prompt.trim()) return;

    // Call your LLM (e.g., OpenAI) to generate a response
    const llmRes = await axios.post('/api/chat', { prompt });

    const text = llmRes.data.reply; // assume reply field
    setResponse(text);

    // Now synthesize the reply
    // We could call the same VoicePlayer logic or pass `text` to it
    // For brevity, we’ll just set `response` and let VoicePlayer read it
  };

  return (
    <div>
      <textarea
        rows={3}
        placeholder="Ask me anything..."
        value={prompt}
        onChange={e => setPrompt(e.target.value)}
        style={{ width: '100%', marginBottom: 8 }}
      />
      <button onClick={handleChat} style={{ width: '100%' }}>
        Ask
      </button>

      {response && <VoicePlayer text={response} />}
    </div>
  );
};

export default ChatWithVoice;

Implementation note – In a real app you’d expose a small backend endpoint /api/chat that proxies to your LLM, keeping your API keys secret.

Deployment Checklist

  1. HTTPS – Voice streaming requires a secure context.
  2. CORS – ElevenLabs sets the correct CORS headers, so you can call it directly from the browser.
  3. Rate Limits – Keep an eye on your usage; the free tier is generous but still limited.
  4. Error Handling – Gracefully handle network failures or API errors to keep the UX smooth.
  5. Accessibility – Add aria-labels and ensure the audio controls are keyboard‑friendly.

Wrap‑Up

By integrating ElevenLabs’ natural‑sounding TTS into a React UI, you can:

  • Deliver instant, high‑quality voice feedback.
  • Personalize the experience with voice cloning.
  • Build conversational agents that feel alive.

All the heavy lifting—speech synthesis, voice model management—is handled by ElevenLabs. Your job is to wire it up, manage the UI, and think about how voice can make your product more inclusive and engaging.

Ready to bring your UI to life with voice? Sign up for ElevenLabs and start building today: https://try.elevenlabs.io/kr07zfuqn1bp. Happy coding!

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.