<AudioPlayer.Root
src={recording.url}
title="Kickoff call"
description="Jane Doe · 30 s"
durationSeconds={recording.seconds}
downloadName="kickoff-call.mp3"
/>The <audio> element stays hidden so every browser draws the same controls.
durationSeconds is shown until the file's own metadata arrives — a webm or a
stream reports Infinity until its end is known, so the figure you already
have stands in.
Driving it from a transcript
ref exposes seek(seconds): it moves the playhead and plays from there,
waiting for the metadata if the file has not loaded yet.
const player = useRef<AudioPlayerHandle>(null);
<AudioPlayer.Root ref={player} src={src} title="Kickoff call" />
<button onClick={() => player.current?.seek(17)}>Next steps</button>Playback held higher up
useAudioPlayback owns one <audio> element's state apart from its controls.
Hold it above your tabs and the recording keeps playing while the controls
showing it mount and unmount — render AudioPlayer.Panel for the full card, or
compose a smaller player from the parts.
const playback = useAudioPlayback({ src, durationSeconds: 12 });
<audio {...playback.element} />
<AudioPlayer.PlayToggle playback={playback} size="icon-sm" />
<AudioPlayer.SpeedToggle rate={playback.rate} onRateChange={playback.chooseRate} />Labels
Every string comes through labels, English by default. rate formats a
speed for its button, so a French player reads 1,5×:
<AudioPlayer.Root
labels={{
play: "Lire",
pause: "Mettre en pause",
rate: (rate) => `${String(rate).replace(".", ",")}×`,
}}
…
/>API
AudioPlayer.Root
| Prop | Type | Default | |
|---|---|---|---|
src | string | required | The file the download button offers; the panel itself plays nothing. |
title | ReactNode | required | |
description | ReactNode | — | |
downloadName | string | — | Offers the file as a download under this name; no button without it. |
durationSeconds | number | null | — | The length the caller already knows, shown until the file's own metadata arrives. |
frame | boolean | — | False inside a card of its own: the controls without their border and padding. |
labels | Partial<AudioPlayerLabels> | — | |
onRateChange | ((rate: number) => void) | — | |
onTimeChange | ((seconds: number) => void) | — | |
playbackRates | readonly number[] | — | |
rate | number | — | A speed held by the caller (a user preference); the player keeps its own otherwise. |
ref | Ref<AudioPlayerHandle> | — | |
skipSeconds | number | — | |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus the DOM props of the element it renders. Source: audio-player/components/audio-player-root.tsx.
AudioPlayer.Panel
| Prop | Type | Default | |
|---|---|---|---|
playback | AudioPlayback | required | |
src | string | required | The file the download button offers; the panel itself plays nothing. |
title | ReactNode | required | |
description | ReactNode | — | |
downloadName | string | — | Offers the file as a download under this name; no button without it. |
frame | boolean | true | False inside a card of its own: the controls without their border and padding. |
labels | Partial<AudioPlayerLabels> | — | |
playbackRates | readonly number[] | DEFAULT_PLAYBACK_RATES | |
skipSeconds | number | 15 | |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <div> prop. Source: audio-player/components/audio-player-panel.tsx.
AudioPlayer.PlayToggle
| Prop | Type | Default | |
|---|---|---|---|
playback | AudioPlayback | required | |
labels | Partial<Pick<AudioPlayerLabels, "pause" | "play">> | — | |
loading | boolean | — | Show a leading spinner and mark the button aria-busy, disabling
interaction while an async action is in flight (e.g. a form submit). |
shape | "default" | "pill" | — | pill forces a fully rounded button; default follows the theme's
--radius, so it changes with the brand. |
size | "icon" | "icon-lg" | "icon-sm" | "icon-lg" | |
variant | "brand" | "default" | "destructive" | "ghost" | "highlight" | "link" | "outline" | "primary" | "secondary" | — | Weight of the action. default is the one thing the page exists for, at
most one per view; secondary an equal-weight alternative; outline
for toolbars and anything that repeats in a row; ghost inside dense
surfaces; destructive for what cannot be undone; link for an action
that reads as navigation. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
className | string | ((state: ButtonState) => string | undefined) | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
focusableWhenDisabled | boolean | — | Whether the button should be focusable when disabled. |
nativeButton | boolean | — | Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>). |
render | ComponentRenderFn<HTMLProps, ButtonState> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
style | CSSProperties | ((state: ButtonState) => CSSProperties | undefined) | — | Style applied to the element, or a function that returns a style object based on the component's state. |
Plus the DOM props of the element it renders. Source: audio-player/components/audio-player-play-toggle.tsx.
AudioPlayer.SeekBar
| Prop | Type | Default | |
|---|---|---|---|
playback | AudioPlayback | required | |
labels | Partial<Pick<AudioPlayerLabels, "position" | "unavailable">> | — | |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
className | string | ((state: SliderRootState) => string | undefined) | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
defaultValue | number | readonly number[] | — | The uncontrolled value of the slider when it's initially rendered. To render a controlled slider, use the value prop instead. |
disabled | boolean | — | Whether the slider should ignore user interaction. |
form | string | — | Identifies the form that owns the slider inputs. Useful when the slider is rendered outside the form. |
format | NumberFormatOptions | — | Options to format the value. |
largeStep | number | — | The granularity with which the slider can step through values when using Page Up/Page Down or Shift + Arrow Up/Arrow Down. |
locale | LocalesArgument | — | The locale used by Intl.NumberFormat when formatting the value.
Defaults to the user's runtime locale. |
minStepsBetweenValues | number | — | The minimum steps between values in a range slider. |
name | string | — | Identifies the field when a form is submitted. |
onValueCommitted | ((value: number | readonly number[], eventDetails: SliderRootCommitEventDetails) => void) | — | Callback function that is fired when a value change is committed.
Does not fire if the value did not change, or if the change was canceled.
Warning: This is a generic event, not a change event. The eventDetails.reason indicates what triggered the commit: |
orientation | Orientation | — | The component orientation. |
render | ComponentRenderFn<HTMLProps, SliderRootState> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
style | CSSProperties | ((state: SliderRootState) => CSSProperties | undefined) | — | Style applied to the element, or a function that returns a style object based on the component's state. |
thumbAlignment | "center" | "edge" | "edge-client-only" | — | How the thumb(s) are aligned relative to Slider.Control when the value is at min or max: |
thumbCollisionBehavior | "none" | "push" | "swap" | — | Controls how thumbs behave when they collide during pointer interactions. |
Plus the DOM props of the element it renders. Source: audio-player/components/audio-player-seek-bar.tsx.
AudioPlayer.SkipButtons
| Prop | Type | Default | |
|---|---|---|---|
playback | AudioPlayback | required | |
labels | Partial<Pick<AudioPlayerLabels, "back" | "forward">> | — | |
seconds | number | 15 | How far one press moves. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
Plus every <div> prop. Source: audio-player/components/audio-player-skip-buttons.tsx.
AudioPlayer.SpeedToggle
| Prop | Type | Default | |
|---|---|---|---|
onRateChange | (rate: number) => void | required | |
rate | number | required | |
labels | Partial<Pick<AudioPlayerLabels, "rate" | "speed">> | — | |
loading | boolean | — | Show a leading spinner and mark the button aria-busy, disabling
interaction while an async action is in flight (e.g. a form submit). |
rates | readonly number[] | DEFAULT_PLAYBACK_RATES | |
shape | "default" | "pill" | — | pill forces a fully rounded button; default follows the theme's
--radius, so it changes with the brand. |
size | "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | — | Height and horizontal padding. The icon-* set is square, for a button
whose entire content is one icon — give those an aria-label. |
variant | "brand" | "default" | "destructive" | "ghost" | "highlight" | "link" | "outline" | "primary" | "secondary" | — | Weight of the action. default is the one thing the page exists for, at
most one per view; secondary an equal-weight alternative; outline
for toolbars and anything that repeats in a row; ghost inside dense
surfaces; destructive for what cannot be undone; link for an action
that reads as navigation. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
className | string | ((state: ButtonState) => string | undefined) | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
focusableWhenDisabled | boolean | — | Whether the button should be focusable when disabled. |
nativeButton | boolean | — | Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>). |
render | ComponentRenderFn<HTMLProps, ButtonState> | ReactElement<unknown, string | JSXElementConstructor<any>> | — | Allows you to replace the component's HTML element
with a different tag, or compose it with another component. Accepts a ReactElement or a function that returns the element to render. |
style | CSSProperties | ((state: ButtonState) => CSSProperties | undefined) | — | Style applied to the element, or a function that returns a style object based on the component's state. |
Plus the DOM props of the element it renders. Source: audio-player/components/audio-player-speed-toggle.tsx.
AudioPlayer.DownloadButton
| Prop | Type | Default | |
|---|---|---|---|
name | string | required | The file name the browser saves under. |
src | string | required | |
labels | Partial<Pick<AudioPlayerLabels, "download">> | — | |
loading | boolean | — | Show a leading spinner and mark the button aria-busy, disabling
interaction while an async action is in flight (e.g. a form submit). |
shape | "default" | "pill" | — | pill forces a fully rounded button; default follows the theme's
--radius, so it changes with the brand. |
size | "default" | "icon" | "icon-lg" | "icon-sm" | "icon-xs" | "lg" | "sm" | "xs" | — | Height and horizontal padding. The icon-* set is square, for a button
whose entire content is one icon — give those an aria-label. |
variant | "brand" | "default" | "destructive" | "ghost" | "highlight" | "link" | "outline" | "primary" | "secondary" | — | Weight of the action. default is the one thing the page exists for, at
most one per view; secondary an equal-weight alternative; outline
for toolbars and anything that repeats in a row; ghost inside dense
surfaces; destructive for what cannot be undone; link for an action
that reads as navigation. |
exit | Exit | — | Inside a <Presence>: plays this child's way out before it unmounts. |
className | string | ((state: ButtonState) => string | undefined) | — | CSS class applied to the element, or a function that returns a class based on the component's state. |
focusableWhenDisabled | boolean | — | Whether the button should be focusable when disabled. |
style | CSSProperties | ((state: ButtonState) => CSSProperties | undefined) | — | Style applied to the element, or a function that returns a style object based on the component's state. |
Plus the DOM props of the element it renders. Source: audio-player/components/audio-player-download-button.tsx.