165

Voice Picker

PreviousNext

Searchable voice selector with audio preview, orb visualization, and ElevenLabs voice integration.

<script setup lang="ts">
import type { ElevenLabs } from '@elevenlabs/elevenlabs-js'
import { VoicePicker } from '@/components/elevenlabs-ui/voice-picker'
import { ref } from 'vue'

const voices: ElevenLabs.Voice[] = [
  {
    voiceId: '21m00Tcm4TlvDq8ikWAM',
    name: 'Rachel',
    category: 'premade',
    labels: {
      accent: 'american',
      descriptive: 'casual',
      age: 'young',
      gender: 'female',
      language: 'en',
      use_case: 'conversational',
    },
    description:
      'Matter-of-fact, personable woman. Great for conversational use cases.',
    previewUrl:
      'https://storage.googleapis.com/eleven-public-prod/premade/voices/21m00Tcm4TlvDq8ikWAM/b4928a68-c03b-411f-8533-3d5c299fd451.mp3',
  },
  {
    voiceId: '29vD33N1CtxCmqQRPOHJ',
    name: 'Drew',
    category: 'premade',
    labels: {
      accent: 'american',
      description: 'well-rounded',
      age: 'middle_aged',
      gender: 'male',
      use_case: 'news',
    },
    previewUrl:
      'https://storage.googleapis.com/eleven-public-prod/premade/voices/29vD33N1CtxCmqQRPOHJ/b99fc51d-12d3-4312-b480-a8a45a7d51ef.mp3',
  },
  {
    voiceId: '2EiwWnXFnvU5JabPnv8n',
    name: 'Clyde',
    category: 'premade',
    labels: {
      accent: 'american',
      descriptive: 'intense',
      age: 'middle_aged',
      gender: 'male',
      language: 'en',
      use_case: 'characters_animation',
    },
    description: 'Great for character use-cases',
    previewUrl:
      'https://storage.googleapis.com/eleven-public-prod/premade/voices/2EiwWnXFnvU5JabPnv8n/65d80f52-703f-4cae-a91d-75d4e200ed02.mp3',
  },
]

const selectedVoice = ref('21m00Tcm4TlvDq8ikWAM')
const open = ref(false)

function handleValueChange(value: string) {
  selectedVoice.value = value
}
</script>

<template>
  <div class="w-full max-w-lg">
    <VoicePicker
      :voices="voices"
      :model-value="selectedVoice"
      :open="open"
      placeholder="Select a voice..."
      @update:model-value="handleValueChange"
      @update:open="open = $event"
    />
  </div>
</template>

Installation

pnpm dlx elevenlabs-ui-vue@latest add voice-picker

Usage

import { VoicePicker } from "@/components/elevenlabs-ui/voice-picker"

Basic Usage

<script setup lang="ts">
import { ElevenLabs } from "@elevenlabs/elevenlabs-js"

const voices = [
  {
    voice_id: "21m00Tcm4TlvDq8ikWAM",
    name: "Rachel",
    preview_url: "https://example.com/rachel-preview.mp3",
    // ... other voice properties
  },
    // ... more voices
]

const selectedVoice = ref("")

function handleValueChange(value: string) {
  selectedVoice.value = value
}
</script>

<template>
  <VoicePicker
    :voices="voices"
    v-model="selectedVoice"
    @update:model-value="handleValueChange"
  />
</template>

Controlled vs Uncontrolled

<script setup lang="ts">
import { VoicePicker } from '@/components/elevenlabs-ui/voice-picker'

const voices = ref([])
const selectedVoice = ref('')
</script>

<template>
  <!-- Controlled -->
  <VoicePicker
    :voices="voices"
    v-model="selectedVoice"
  />

  <!-- Uncontrolled -->
  <VoicePicker
    :voices="voices"
    @update:model-value="(voiceId) => console.log('Selected:', voiceId)"
  />
</template>

Control Open State

<script setup lang="ts">
const voices = ref([])
const selectedVoice = ref('')
const open = ref(false)

function handleValueChange(value: string) {
  selectedVoice.value = value
}
</script>

<template>
  <VoicePicker
    :voices="voices"
    v-model="selectedVoice"
    :open="open"
    @update:model-value="handleValueChange"
    @update:open="open = $event"
  />
</template>

Custom Placeholder

<template>
  <VoicePicker
    :voices="voices"
    v-model="selectedVoice"
    placeholder="Choose a voice..."
  />
</template>

Fetching Voices from ElevenLabs API

<script setup lang="ts">
import { ElevenLabsClient } from '@elevenlabs/elevenlabs-js'

const voices = ref<Voice[]>([])
const selectedVoice = ref('')

onMounted(async () => {
  const client = new ElevenLabsClient({
    apiKey: import.meta.env.ELEVENLABS_API_KEY,
  })

  const response = await client.voices.getAll()
  voices.value = response.voices
})
</script>

<template>
  <VoicePicker
    :voices="voices"
    v-model="selectedVoice"
  />
</template>

API Reference

VoicePicker

A searchable dropdown for selecting ElevenLabs voices with audio preview and orb visualization.

Props

PropTypeDefaultDescription
voicesElevenLabs.Voice[]-Required. Array of ElevenLabs voices
modelValuestring-Selected voice ID (controlled)
placeholderstring"Select a voice..."Placeholder text when no voice selected
classstring-Optional CSS classes for the trigger button
openboolean-Control popover open state

Emits

EventTypeDescription
update:modelValue(value: string) => voidCallback when selection changes
update:open(open: boolean) => voidCallback when popover open state changes

Features

  • Search Functionality: Filter voices by name with built-in search
  • Audio Preview: Play voice samples with play/pause controls
  • Orb Visualization: Visual representation of each voice with the Orb component
  • Keyboard Navigation: Full keyboard support for accessibility
  • Controlled/Uncontrolled: Supports both controlled and uncontrolled patterns
  • ElevenLabs Integration: Works seamlessly with ElevenLabs Voice API
  • Audio Player: Integrated audio playback with shared state management

Notes

  • Built on top of Command, Popover, and AudioPlayer components
  • Requires @elevenlabs/elevenlabs-js for ElevenLabs Voice types
  • Each voice displays with an Orb visualization and preview audio
  • Audio playback is managed by AudioPlayer for consistent state
  • Search is case-insensitive and filters by voice name
  • Supports both controlled (modelValue/onUpdate:modelValue) and uncontrolled modes
  • Open state can be controlled externally via open/onUpdate:open prop and event
  • Keyboard accessible with standard combobox patterns
  • Preview URLs from ElevenLabs Voice objects are used for audio playback