Skip to content

Quickstart

Add a voice button to an existing Next.js textarea with OpenAI.

Terminal window
npm install @voiceinput/react @voiceinput/openai

Create .env.local with your OpenAI key and the exact origin of your app:

OPENAI_API_KEY=your-openai-api-key
APP_ORIGIN=http://localhost:3000

Keep OPENAI_API_KEY server-only. Never give it a NEXT_PUBLIC_ prefix.

src/app/api/voice-token/route.ts
import { createOpenAITokenHandler } from "@voiceinput/openai/server";
import { getCurrentUser } from "@/lib/auth"; // your existing session check
const appOrigin = new URL(process.env.APP_ORIGIN!).origin;
export const POST = createOpenAITokenHandler({
apiKey: process.env.OPENAI_API_KEY!,
authorize: async (request) => {
if (request.headers.get("origin") !== appOrigin) return null;
const user = await getCurrentUser(request);
return user ? { subject: user.id } : null;
},
});

Returning null produces a 401 without issuing a credential. Replace getCurrentUser with the session check already used by your app.

src/app/composer.tsx
"use client";
import { useState } from "react";
import { openai } from "@voiceinput/openai";
import { getVoiceInputErrorMessage, useVoiceInput } from "@voiceinput/react";
const provider = openai({ tokenEndpoint: "/api/voice-token" });
export function Composer() {
const [message, setMessage] = useState("");
const voice = useVoiceInput({
provider,
value: message,
onValueChange: setMessage,
});
const active = voice.status !== "idle" && voice.status !== "error";
return (
<div>
<label htmlFor="message">Message</label>
<textarea
id="message"
ref={voice.targetRef}
value={message}
onChange={(event) => setMessage(event.currentTarget.value)}
/>
<button {...voice.getTriggerProps()}>{active ? "Stop" : "Speak"}</button>
<output aria-live="polite">{voice.status}</output>
{voice.error ? (
<p role="alert">{getVoiceInputErrorMessage(voice.error)}</p>
) : null}
</div>
);
}

Render Composer from a page that your signed-in users can access.

Start the Next.js development server, open the app, and press Speak. Allow microphone access and dictate into the textarea. Restart the server after changing .env.local.

If the route returns 401, check the session and APP_ORIGIN. For other failures, use the troubleshooting guide.