VoicePicker
StableA list of voices to hear and choose from, with tag filters. Only one sample plays at a time.
Also known as voice selector, voice library
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
With tag filters
Narrator voice
Warm and unhurried. Good for long reads.
Built inCalmNarration
Bright and quick. Good for short updates.
Built inUpbeat
Low and steady.
DesignedCalmDeep
Clear and patient, for help lines.
From the librarySupportCalm
Shared by Rosa Lindqvist, licensed
Available until 3 March 2027
Recorded on 2 October.
ClonedNarration
Your voice, verified
const [value, setValue] = useState("juniper");
<VoicePicker label="Narrator voice" voices={voices} value={value} onChange={setValue} filters />
// voices: [{ id: "juniper", name: "Juniper", description: "Warm and unhurried.", tags: ["Calm", "Narration"],
// source: "builtIn", sampleSeconds: 8 }, ...]Filter by tag, play samples, and choose. Starting a sample stops the one playing, and a filter that hides the playing voice stops it too.
When to use it
Helps people choose a voice by hearing it, with ownership and availability in view, without samples talking over each other.
Made of: Label · Filter chips (optional) · Voice cards · Empty or no-match message
Use it for
- Choosing the voice for an assistant, a narrator or a phone agent.
- A voice library where people filter by accent, mood or use case.
- A settings page that lists the person's own designed and cloned voices.
Not for
- Showing one voice on its own: use VoiceCard.
- Choosing among a few short text options: use RadioGroup.
- Choosing which AI model answers: use ModelPicker.
Do and avoid
Narrator voice
Warm and unhurried.
Built inCalm
Bright and quick.
Built inUpbeat
Do
- Put the most likely voices first, and keep the list short enough to compare by ear.
- Use tags people understand: mood, accent, use case.
- Keep a withdrawn voice in the list while someone still uses it, so they know to switch.
Avoid
- Do not play a sample when a voice is chosen. Choosing and hearing are separate.
- Do not use colour alone for locked or withdrawn voices. The card writes the reason.
- Do not make the filter chips lime or amber. They are neutral controls.
On a phone
- Always one column on a phone. layout="grid" adds a second column only from 640px wide.
- Filter chips wrap and keep a 44px tap area.
- Sample buttons are 44px on a touch screen, and a tap on the card chooses the voice.
Seen in products
- Claude voice mode: click a voice to preview it, click again to stop.
- ElevenLabs Voice Library filters voices by tags such as accent, age and use case, and shows a notice period when an owner withdraws a voice.
- GPT-Live-1 offers 12 voices, Gemini native audio 30 HD voices, and Copilot named voices (Meadow, Wave, Grove, Canyon).
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| voicesrequired | VoiceOption[] | none | Each has id, name, source, and optional description, tags, owner, consent, status, withdrawsOn, lockedReason, sampleSeconds and sampleFails. |
| value | string | none | Id of the chosen voice. |
| onChangerequired | (id: string) => void | none | Called when a voice is chosen. Never plays its sample. |
| label | string | "Voice" | Name of the group, shown above the list. |
| hideLabel | boolean | none | Hides the label visually. Screen readers still hear it. |
| filters | boolean | false | Shows filter chips built from the tags, with All first. |
| loading | boolean | false | Shows three placeholder rows. |
| layout | "list" | "grid" | "list" | Grid adds a second column from 640px wide. |
| previewFor | (voice: VoiceOption) => VoicePreview | none | Your own player. Leave out to use the stand-in, which simulates samples with a timer. |
| emptyText | string | "No voices yet." | Shown when there are no voices at all. |
| className | string | none | Extra classes for the wrapper. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one VoicePicker switches all of this from its properties panel, across 16 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| Hide label | hideLabel | On or off |
| Filters | filters | On or off |
| Layout | layout | List, Grid |
| Loading | loading | On or off |
| Label | label | Any text |
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- loading
- Three placeholder rows and a busy list. Set loading.
- empty
- A dashed box with emptyText when voices is empty.
- no match
- A filter shows no voices: "No voices tagged Calm." with Show all voices.
- previewing
- One card plays its sample; starting another stops it.
- filtered
- A filter chip is pressed and the list shows only voices with that tag.
Tokens
Change one of these and every part that uses it follows.
--surface--border--fg--fg-muted--radius-card--radius-control--dur-fast
Built in
- The list is a radiogroup named by the label. Each card is a native radio, so arrow keys move and choose and Tab stops on the chosen one.
- Locked and withdrawn voices are disabled radios, skipped by the arrow keys.
- Filter chips are toggle buttons that say whether they are pressed. After filtering, a polite message says how many voices are shown.
- While loading, the list is marked busy and "Loading voices" is announced.
- Only one sample plays at a time, and a sample hidden by a filter stops.
What you need to do
- Give the picker a label that says what the voice is for, such as "Narrator voice".
- If you pass previewFor, stop the playing sample before starting another, and never start one without a press.
- Write withdrawal dates in full with your own date code.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide