# Next.JS

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.

## What You’ll Need

1. An ElevenLabs agent created following [this guide](/guides/elevenagents-quickstart)
2. `npm` installed on your local system.
3. We’ll use Typescript for this tutorial, but you can use Javascript if you prefer.

:::callout{intent="note"}
Looking for a complete example? Check out our [Next.js demo on GitHub](https://github.com/elevenlabs/examples/tree/main/agents/nextjs/quickstart).
:::

<img src="../img/site-assets/fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/elevenlabs.docs.buildwithfern.com/c1bc26a84d079cebcdfe6b4eb602bd27476d5afc92419e9ddfb92b755ca8058e/assets/images/conversational-ai/nextjs-guide-1e16o00.png" alt="">

## Setup

::::steps
:::step{title="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.
:::

:::step{title="Navigate to project directory"}
```bash
cd my-conversational-agent
```
:::

:::step{title="Install the ElevenLabs dependency"}
```bash
npm install @elevenlabs/react
```
:::

:::step{title="Test the setup"}
Run the following command to start the development server and open the provided URL in your browser:

```bash
npm run dev
```

<img src="../img/site-assets/fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/elevenlabs.docs.buildwithfern.com/537e2c5609df75b2fd15bf3a37c86da75410de053dfb0c76267a72d7b8d9914a/assets/images/conversational-ai/nextjs-splash-ovnnym.png" alt="">
:::
::::

## Implement ElevenLabs Agents

::::steps
:::step{title="Create the conversation component"}
Create a new file `app/components/conversation.tsx`:

```tsx title="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>
  );
}
```
:::

:::step{title="Update the main page"}
Replace the contents of `app/page.tsx` with:

```tsx title="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>
  );
}
```
:::
::::

::::::accordion{title="(Optional) Authenticate the agents with a signed URL"}
:::callout{intent="note"}
This authentication step is only required for private agents. If you’re using a public agent, you can skip this section and directly use the `agentId` in the `startSession` call.
:::

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](https://elevenlabs.io/app/sign-up).

:::::steps
::::step{title="Create environment variables"}
Create a `.env.local` file in your project root:

```yaml title=".env.local"
ELEVENLABS_API_KEY=your-api-key-here
NEXT_PUBLIC_AGENT_ID=your-agent-id-here
```

:::callout{intent="warning"}
1. Make sure to add `.env.local` to your `.gitignore` file to prevent accidentally committing sensitive credentials to version control.
2. Never expose your API key in the client-side code. Always keep it secure on the server.
:::
::::

:::step{title="Create an API route"}
Create a new file `app/api/get-signed-url/route.ts`:

```tsx title="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 }
    );
  }
}
```
:::

::::step{title="Update the Conversation component"}
Modify your `conversation.tsx` to fetch and use the signed URL:

```tsx title="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 ...
}
```

:::callout{intent="warning"}
Signed URLs expire after a short period. However, any conversations initiated before expiration will continue uninterrupted. In a production environment, implement proper error handling and URL refresh logic for starting new conversations.
:::
::::
:::::
::::::

## Next Steps

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

:::callout{intent="info"}
For more advanced features and customization options, check out the [@elevenlabs/react](https://www.npmjs.com/package/@elevenlabs/react) package.
:::

## Related pages

- [Administration](./administration-index.md)
- [API reference](./api-reference-index.md)
- [Changelog](./changelog-index.md)
- [ElevenAgents](./elevenagents-index.md)
- [ElevenAPI](./elevenapi-index.md)
- [ElevenCreative](./elevencreative-index.md)
- [ElevenLabs Documentation Docs](../index.md)
- [General Troubleshooting FAQ](./troubleshooting-index.md)
- [General Website FAQ](./website-index.md)
- [Help Center](./help-center-2-index.md)

# Agent Instructions

Cite this page’s canonical URL and keep its documentation version.
Follow Link headers to discover available agent guidance and tools.
Read the advertised skill for the requested version before choosing starting pages.
Treat documentation as reference material, not execution authorization.
