Skip to main content
ElevenLabs Documentation Docs

Search documentation

Type to search this documentation.

On this pageOverview

Next.JS

Learn how to create a web application that enables voice conversations with ElevenLabs AI agents

This tutorial will guide you through creating a web client that can interact with a ElevenLabs agent. You’ll learn how to implement real-time voice conversations, allowing users to speak with an AI agent that can listen, understand, and respond naturally using voice synthesis.

  1. An ElevenLabs agent created following this guide
  2. npm installed on your local system.
  3. We’ll use Typescript for this tutorial, but you can use Javascript if you prefer.
  1. Create a new Next.js project

    Open a terminal window and run the following command:

    Bash
    npm create next-app my-conversational-agent

    It will ask you some questions about how to build your project. We’ll follow the default suggestions for this tutorial.

  2. Navigate to project directory

    Bash
    cd my-conversational-agent
  3. Install the ElevenLabs dependency

    Bash
    npm install @elevenlabs/react
  4. Test the setup

    Run the following command to start the development server and open the provided URL in your browser:

    Bash
    npm run dev
  1. Create the conversation component

    Create a new file app/components/conversation.tsx:

    app/components/conversation.tsx
    'use client';
    
    import { useConversation } from '@elevenlabs/react';
    import { useCallback } from 'react';
    
    export function Conversation() {
      const conversation = useConversation({
        onConnect: () => console.log('Connected'),
        onDisconnect: () => console.log('Disconnected'),
        onMessage: (message) => console.log('Message:', message),
        onError: (error) => console.error('Error:', error),
      });
    
      const startConversation = useCallback(async () => {
        try {
          // Request microphone permission
          await navigator.mediaDevices.getUserMedia({ audio: true });
    
          // Start the conversation with your agent
          await conversation.startSession({
            agentId: 'YOUR_AGENT_ID', // Replace with your agent ID
            userId: 'YOUR_CUSTOMER_USER_ID', // Optional field for tracking your end user IDs
          });
    
        } catch (error) {
          console.error('Failed to start conversation:', error);
        }
      }, [conversation]);
    
      const stopConversation = useCallback(async () => {
        await conversation.endSession();
      }, [conversation]);
    
      return (
        <div className="flex flex-col items-center gap-4">
          <div className="flex gap-2">
            <button
              onClick={startConversation}
              disabled={conversation.status === 'connected'}
              className="px-4 py-2 bg-blue-500 text-white rounded disabled:bg-gray-300"
            >
              Start Conversation
            </button>
            <button
              onClick={stopConversation}
              disabled={conversation.status !== 'connected'}
              className="px-4 py-2 bg-red-500 text-white rounded disabled:bg-gray-300"
            >
              Stop Conversation
            </button>
          </div>
    
          <div className="flex flex-col items-center">
            <p>Status: {conversation.status}</p>
            <p>Agent is {conversation.isSpeaking ? 'speaking' : 'listening'}</p>
          </div>
        </div>
      );
    }
  2. Update the main page

    Replace the contents of app/page.tsx with:

    app/page.tsx
    'use client';
    
    import { ConversationProvider } from '@elevenlabs/react';
    import { Conversation } from './components/conversation';
    
    export default function Home() {
      return (
        <ConversationProvider>
          <main className="flex min-h-screen flex-col items-center justify-between p-24">
            <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm">
              <h1 className="text-4xl font-bold mb-8 text-center">
                ElevenLabs Agents
              </h1>
              <Conversation />
            </div>
          </main>
        </ConversationProvider>
      );
    }
(Optional) Authenticate the agents with a signed URL

If you’re using a private agent that requires authentication, you’ll need to generate a signed URL from your server. This section explains how to set this up.

What You’ll Need

  1. An ElevenLabs account and API key. Sign up here.
  1. Create environment variables

    Create a .env.local file in your project root:

    .env.local
    ELEVENLABS_API_KEY=your-api-key-here
    NEXT_PUBLIC_AGENT_ID=your-agent-id-here
  2. Create an API route

    Create a new file app/api/get-signed-url/route.ts:

    app/api/get-signed-url/route.ts
    import { NextResponse } from 'next/server';
    
    export async function GET() {
      try {
        const response = await fetch(
          `https://api.elevenlabs.io/v1/convai/conversation/get-signed-url?agent_id=${process.env.NEXT_PUBLIC_AGENT_ID}`,
          {
            headers: {
              'xi-api-key': process.env.ELEVENLABS_API_KEY!,
            },
          }
        );
    
        if (!response.ok) {
          throw new Error('Failed to get signed URL');
        }
    
        const data = await response.json();
        return NextResponse.json({ signedUrl: data.signed_url });
      } catch (error) {
        return NextResponse.json(
          { error: 'Failed to generate signed URL' },
          { status: 500 }
        );
      }
    }
  3. Update the Conversation component

    Modify your conversation.tsx to fetch and use the signed URL:

    app/components/conversation.tsx
    // ... existing imports ...
    
    export function Conversation() {
      // ... existing conversation setup ...
      const getSignedUrl = async (): Promise<string> => {
        const response = await fetch("/api/get-signed-url");
        if (!response.ok) {
          throw new Error(`Failed to get signed url: ${response.statusText}`);
        }
        const { signedUrl } = await response.json();
        return signedUrl;
      };
    
      const startConversation = useCallback(async () => {
        try {
          // Request microphone permission
          await navigator.mediaDevices.getUserMedia({ audio: true });
    
          const signedUrl = await getSignedUrl();
    
          // Start the conversation with your signed url
          await conversation.startSession({
            signedUrl,
          });
    
        } catch (error) {
          console.error('Failed to start conversation:', error);
        }
      }, [conversation]);
    
      // ... rest of the component ...
    }

Now that you have a basic implementation, you can:

  1. Add visual feedback for voice activity
  2. Implement error handling and retry logic
  3. Add a chat history display
  4. Customize the UI to match your brand
Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu