Audio Player
Headless audio player: the shared MediaPlayer controls under AudioPlayer* names plus the native <audio> part. Shared types and helpers are exported from @vizejs/ui/media-player.
|
|
| Package |
@vizejs/ui/audio-player |
| Maturity |
stable |
| Own the source |
vize lib pull audio-player |
| Requires |
context, media-player |
| Aliases |
audio player, audio, podcast player, music player |
| Covers |
HTML audio, Vidstack Player, Media Chrome |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { AudioPlayerAudio, AudioPlayerCaptionsButton, AudioPlayerLoadingIndicator, AudioPlayerMuteButton, AudioPlayerPlayButton, AudioPlayerPlaybackRateButton, AudioPlayer, AudioPlayerRoot, AudioPlayerSeekSlider, AudioPlayerTimeDisplay, AudioPlayerVolumeSlider } from "@vizejs/ui/audio-player";
Or copy the source into your project with vize lib pull audio-player (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Podcast player wiring a native audio element to play, seek, time, mute, and volume controls. -->
<script setup lang="ts">
import {
AudioPlayer,
AudioPlayerAudio,
AudioPlayerMuteButton,
AudioPlayerPlayButton,
AudioPlayerSeekSlider,
AudioPlayerTimeDisplay,
AudioPlayerVolumeSlider,
} from "@vizejs/ui/audio-player";
</script>
<template>
<AudioPlayer aria-label="Episode 42: Designing headless components">
<AudioPlayerAudio src="/media/episode-42.mp3" preload="none" />
<AudioPlayerPlayButton v-slot="{ paused }">{{
paused ? "Play" : "Pause"
}}</AudioPlayerPlayButton>
<AudioPlayerTimeDisplay mode="current" />
<AudioPlayerSeekSlider />
<AudioPlayerTimeDisplay mode="duration" />
<AudioPlayerMuteButton v-slot="{ muted }">{{
muted ? "Unmute" : "Mute"
}}</AudioPlayerMuteButton>
<AudioPlayerVolumeSlider />
</AudioPlayer>
</template>
API
AudioPlayerAudio
Source: src/families/media/audio-player/audio-player-audio.vue
Props
| Prop |
Type |
Default |
Description |
src |
string |
undefined |
Audio source, validated by the shared media-source policy. Unsafe sources are dropped (data-invalid-src); use <source> children for format fallbacks. |
preload |
MediaPlayerPreload |
"metadata" |
Native preload hint. |
autoplay |
boolean |
false |
Native autoplay. Browsers may block audible autoplay until the user interacts. |
loop |
boolean |
false |
Restart at the end. |
controls |
boolean |
false |
Also render the browser's native controls. |
crossOrigin |
MediaPlayerCrossOrigin |
undefined |
Native CORS policy, required for cross-origin captions. |
allowInsecure |
boolean |
false |
Permit unencrypted http: sources for local development. |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
<source> and <track> children, plus fallback content. |
Exposed
| Member |
Type |
Description |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/media/media-player/media-player-captions-button.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string | null |
undefined |
Accessible name override. undefined uses the state-dependent label from the root messages; null omits aria-label so visible slot text names the button. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to skip it. |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Button content, e.g. an icon. Receives the playback state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
AudioPlayerLoadingIndicator
Source: src/families/media/media-player/media-player-loading-indicator.vue
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Indicator content, rendered only while loading. Defaults to messages.loading. |
Source: src/families/media/media-player/media-player-mute-button.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string | null |
undefined |
Accessible name override. undefined uses the state-dependent label from the root messages; null omits aria-label so visible slot text names the button. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to skip it. |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Button content, e.g. an icon. Receives the playback state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/media/media-player/media-player-play-button.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string | null |
undefined |
Accessible name override. undefined uses the state-dependent label from the root messages; null omits aria-label so visible slot text names the button. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to skip it. |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Button content, e.g. an icon. Receives the playback state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Source: src/families/media/media-player/media-player-playback-rate-button.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string | null |
undefined |
Accessible name override. undefined uses the state-dependent label from the root messages; null omits aria-label so visible slot text names the button. |
rates |
readonly number[] |
[0.5, 1, 1.25, 1.5, 2] |
Rates cycled on click, in order. The next rate after the current one is chosen; an unknown current rate selects the first. |
Events
| Event |
Payload |
Description |
click |
[nativeEvent: MouseEvent] |
Fired before the action. Call preventDefault() to skip it. |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Button content, e.g. an icon. Receives the playback state. |
Exposed
| Member |
Type |
Description |
disabled |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
AudioPlayer
Source: src/families/media/media-player/media-player-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
volume |
number |
undefined |
Controlled volume from 0 to 1 (v-model:volume). |
defaultVolume |
number |
1 |
Initial volume for uncontrolled use. |
muted |
boolean |
undefined |
Controlled muted state (v-model:muted). |
defaultMuted |
boolean |
false |
Initial muted state for uncontrolled use. Rendered as the native muted attribute, so muted autoplay works before hydration. |
playbackRate |
number |
undefined |
Controlled playback rate (v-model:playbackRate). |
defaultPlaybackRate |
number |
1 |
Initial playback rate for uncontrolled use. |
currentTime |
number |
undefined |
Synchronized playback position (v-model:currentTime). The media element stays the source of truth: timeupdate emits the position, and a parent value more than half a second away from it seeks. |
keyboardShortcuts |
boolean |
true |
Handle media keyboard shortcuts while focus is inside the player. |
seekStep |
number |
5 |
Seconds moved by arrow keys on the seek slider and root shortcuts. |
skipStep |
number |
10 |
Seconds skipped by the J and L shortcuts. |
volumeStep |
number |
0.05 |
Volume change for arrow keys, from 0 to 1. |
messages |
MediaPlayerMessages |
undefined |
Localized accessible labels for every control part. Missing keys use English defaults. |
dir |
"ltr" | "rtl" |
"ltr" |
Reading direction used by horizontal sliders and arrow keys. |
ariaLabel |
string |
undefined |
Accessible player name; adds role="group". |
ariaLabelledby |
string |
undefined |
Ids that name the player; adds role="group". |
Events
| Event |
Payload |
Description |
update:volume |
[volume: number] |
Fired when the volume requests a new controlled value. |
update:muted |
[muted: boolean] |
Fired when the muted state requests a new controlled value. |
update:playbackRate |
[rate: number] |
Fired when the playback rate requests a new controlled value. |
update:currentTime |
[time: number] |
Fired on every native timeupdate and seek with the current position. |
play |
[] |
Fired after playback starts. |
pause |
[] |
Fired after playback pauses. |
ended |
[] |
Fired after playback reaches the end. |
playRejected |
[reason: unknown] |
Fired when play() is rejected, e.g. by an autoplay policy. |
error |
[code: number | null] |
Fired when the media element reports an error (MediaError.code). |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Media element and controls. Receives the complete playback state. |
Exposed
| Member |
Type |
Description |
buffered |
— |
|
captionTrack |
— |
|
currentTime |
— |
|
duration |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
error |
— |
|
fullscreen |
— |
|
loading |
— |
|
media |
— |
|
muted |
— |
|
pause |
— |
|
paused |
— |
|
pictureInPicture |
— |
|
play |
— |
|
playbackRate |
— |
|
seek |
— |
|
seekBy |
— |
|
seeking |
— |
|
setCaptionTrack |
— |
|
setMuted |
— |
|
setPlaybackRate |
— |
|
setVolume |
— |
|
state |
— |
|
textTracks |
— |
|
toggleCaptions |
— |
|
toggleFullscreen |
— |
|
togglePictureInPicture |
— |
|
togglePlay |
— |
|
volume |
— |
|
AudioPlayerRoot
Source: src/families/media/media-player/media-player-root.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
volume |
number |
undefined |
Controlled volume from 0 to 1 (v-model:volume). |
defaultVolume |
number |
1 |
Initial volume for uncontrolled use. |
muted |
boolean |
undefined |
Controlled muted state (v-model:muted). |
defaultMuted |
boolean |
false |
Initial muted state for uncontrolled use. Rendered as the native muted attribute, so muted autoplay works before hydration. |
playbackRate |
number |
undefined |
Controlled playback rate (v-model:playbackRate). |
defaultPlaybackRate |
number |
1 |
Initial playback rate for uncontrolled use. |
currentTime |
number |
undefined |
Synchronized playback position (v-model:currentTime). The media element stays the source of truth: timeupdate emits the position, and a parent value more than half a second away from it seeks. |
keyboardShortcuts |
boolean |
true |
Handle media keyboard shortcuts while focus is inside the player. |
seekStep |
number |
5 |
Seconds moved by arrow keys on the seek slider and root shortcuts. |
skipStep |
number |
10 |
Seconds skipped by the J and L shortcuts. |
volumeStep |
number |
0.05 |
Volume change for arrow keys, from 0 to 1. |
messages |
MediaPlayerMessages |
undefined |
Localized accessible labels for every control part. Missing keys use English defaults. |
dir |
"ltr" | "rtl" |
"ltr" |
Reading direction used by horizontal sliders and arrow keys. |
ariaLabel |
string |
undefined |
Accessible player name; adds role="group". |
ariaLabelledby |
string |
undefined |
Ids that name the player; adds role="group". |
Events
| Event |
Payload |
Description |
update:volume |
[volume: number] |
Fired when the volume requests a new controlled value. |
update:muted |
[muted: boolean] |
Fired when the muted state requests a new controlled value. |
update:playbackRate |
[rate: number] |
Fired when the playback rate requests a new controlled value. |
update:currentTime |
[time: number] |
Fired on every native timeupdate and seek with the current position. |
play |
[] |
Fired after playback starts. |
pause |
[] |
Fired after playback pauses. |
ended |
[] |
Fired after playback reaches the end. |
playRejected |
[reason: unknown] |
Fired when play() is rejected, e.g. by an autoplay policy. |
error |
[code: number | null] |
Fired when the media element reports an error (MediaError.code). |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Media element and controls. Receives the complete playback state. |
Exposed
| Member |
Type |
Description |
buffered |
— |
|
captionTrack |
— |
|
currentTime |
— |
|
duration |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
error |
— |
|
fullscreen |
— |
|
loading |
— |
|
media |
— |
|
muted |
— |
|
pause |
— |
|
paused |
— |
|
pictureInPicture |
— |
|
play |
— |
|
playbackRate |
— |
|
seek |
— |
|
seekBy |
— |
|
seeking |
— |
|
setCaptionTrack |
— |
|
setMuted |
— |
|
setPlaybackRate |
— |
|
setVolume |
— |
|
state |
— |
|
textTracks |
— |
|
toggleCaptions |
— |
|
toggleFullscreen |
— |
|
togglePictureInPicture |
— |
|
togglePlay |
— |
|
volume |
— |
|
AudioPlayerSeekSlider
Source: src/families/media/media-player/media-player-seek-slider.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name. Defaults to the root messages.seek. |
ariaLabelledby |
string |
undefined |
Ids that name the slider instead of aria-label. |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Thumb content. Receives the playback state. |
Exposed
| Member |
Type |
Description |
dragging |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
thumb |
— |
|
AudioPlayerTimeDisplay
Source: src/families/media/media-player/media-player-time-display.vue
Props
| Prop |
Type |
Default |
Description |
mode |
MediaPlayerTimeDisplayMode |
"current" |
Which time to show: the position, the duration, or the time remaining (rendered with a leading minus sign). |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState & { readonly text: string; readonly seconds: number } |
Custom rendering. Receives the formatted text, raw seconds, and playback state. |
AudioPlayerVolumeSlider
Source: src/families/media/media-player/media-player-volume-slider.vue
Props
| Prop |
Type |
Default |
Description |
ariaLabel |
string |
undefined |
Accessible name. Defaults to the root messages.volume. |
ariaLabelledby |
string |
undefined |
Ids that name the slider instead of aria-label. |
Slots
| Slot |
Slot props |
Description |
default |
MediaPlayerSlotState |
Thumb content. Receives the playback state. |
Exposed
| Member |
Type |
Description |
dragging |
— |
|
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
focus |
— |
|
thumb |
— |
|
Behavior
Normative state x input -> outcome table for audio-player-audio.vue
(@vizejs/ui/audio-player). The shared controls re-exported as AudioPlayer*
follow media-player.behavior.md. Every row is proven by the named test.
| ID |
State |
Input |
Outcome |
Evidence |
| AP1 |
render |
props |
native <audio> with validated src, preload, loop, controls, and slot <source>s, registered as audio |
renders a native audio element registered as audio media |
| AP2 |
registered |
controls |
slot-labelled play controls and time displays drive the element |
slot-labelled controls drive audio playback and time display |
| AP3 |
unsafe source |
render |
the source is dropped and marked with data-invalid-src |
unsafe audio sources are dropped and marked |
| AP4 |
registered |
part unmounts |
the root unregisters the element and clears data-media-kind |
unmounting the audio part unregisters it from the root |
| AP5 |
SSR |
isolated requests |
markup is byte-identical |
renders byte-identical audio player markup across isolated SSR requests |
| AP6 |
SSR / hydration |
hydrate |
server markup hydrates without warnings or node replacement |
hydrates audio player markup without warnings or node replacement |
| AP7 |
types |
compile |
the root and controls are the shared MediaPlayer parts; no video part is exported |
audio-player.types.test-d.ts |