npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vue-renderless-audio

v0.1.2

Published

Build your own audio player using a renderless Vue component

Downloads

4

Readme

vue-renderless-audio

A completely renderless Vue (2.x) component to help you build custom audio players.

⚠️ This component is still in heavy development and should not be used in any production environment (or at your own risk)!

[TOC]

What does "renderless" mean?

A renderless component only provides functionality and leaves you in full control over markup and styling.

That said, it's important to understand that this component does not provide any UI whatsoever!

Install

# npm
$ npm install vue-renderless-audio --save

# yarn
$ yarn add vue-renderless-audio

Setup

Register the component either globally or locally

// Global registration
import Vue from 'vue'
import RenderlessAudio from 'vue-renderless-audio'
Vue.component('renderless-audio', RenderlessAudio)
// Local registration
import RenderlessAudio from 'vue-renderless-audio'
export default {
    components: {
        RenderlessAudio
    },
    
    ...
}

Basic usage in your markup looks like this

<renderless-audio src="/my-audio-file.mp3">
    <template slot-scope="{ controls, time, state }">

        <!-- Start building your UI here -->

    </template>    
</renderless-audio>

Example implementation

This snippet only shows the general idea behind this component and does not include any real styling yet:

<template>
    <renderless-audio 
        :src="sources" 
        :autoplay="true"
        :loop="true"
        :volume.sync="globalVolume"
        @canplaythrough="doSomething()">

        <div slot-scope="{ controls, time, state }" class="my-music-player">
            <!--
                Progress bar as a range slider, bind progress as value and
                listen for the input event to seek to a different position -->
            <input 
                type="range" 
                :value="state.progress" 
                @input="controls.seek($event.target.value)"
                min="0" max="100" step="0.1" />

            <!-- 
                Control playback with provided functions in the 'controls' object.
                Conditionally display Play or Pause, depending on the current state -->
            <button v-if="!state.isPlaying" @click="controls.play()">Play</button>
            <button v-else @click="controls.pause()">Pause</button>

            <!-- Mute/Unmute control -->
            <button @click="controls.toggleMuted()">Mute/Unmute</button>

            <!-- 
                Volume control as a range slider, bind current volume as value and 
                listen for the input event to set it -->
            <input
                type="range" 
                :value="state.volume" 
                @input="controls.setVolume($event.target.value)" 
                min="0" max="1" step="0.1" />

        </div>

    </renderless-audio>
</template>
<script>
import RenderlessAudio from 'vue-renderless-audio'
export default {
    components: {
        RenderlessAudio
    },

    data() {
        return {
            // Control volume globally from parent (using .sync-modifier on prop)
            globalVolume: 0.5,
        }
    }

    computed: {
        sources() {
            return [
                { src: '/some-audio-file.mp3', type: 'audio/mp3' },
                { src: '/some-audio-file.wav', type: 'audio/wav' }
            ]
        }
    }
}
</script>

Documentation

Props

src (required) The source of your audio file as a string or as an object array for multiple file types (containing src and type properties).

  • type: String or Array
  • default: null
<!-- Single file type -->
<renderless-audio src="/some-audio-file.mp3"></renderless-audio>

<!-- Multiple file types -->
<renderless-audio :src="[
    { src: '/some-audio-file.mp3', type: 'audio/mp3' },
    { src: '/some-audio-file.ogg', type: 'audio/ogg' }
]"></renderless-audio>

muted Sets player muted attribute

  • type: Boolean
  • default: false

autoplay Sets player autoplay attribute

  • type: Boolean
  • default: false

Be aware of browsers autoplay policies. In most modern browsers, some kind of user action (eg. click) is required to autoplay media.

loop Sets player loop attribute

  • type: Boolean
  • default: false

volume Sets player volume, must be a floating digit between 0 and 1. (eg. 0.75 stands for 75% volume)

  • type: Number
  • default: 1

playbackRate Sets the speed at which the media is being played back. The normal playback rate is multiplied by this value to obtain the current rate, so a value of 1.0 indicates normal speed.

  • type: Number
  • default: 1

start Offsets the position where playback begins (in seconds). Must be greater than or equals 0.

  • type: Number
  • default: 0

preload Sets the player preload attribute. Hints to the browser how or if to preload data. Must be 'none' 'metadata' or 'auto'

  • type: `String``
  • default: 'auto'

crossorigin Sets the player crossorigin attribute. Lets you configure the CORS requests for the element's fetched data. Must be 'anonymous' or 'use-credentials'

  • type: `String``
  • default: null

native Shows the native audio player if set to true. Most likely you would only want to use this for development and debugging purposes.

  • type: Boolean
  • default: false

Sync props back to parent

For some props it's useful to have "two-way-binding". You can use the .sync modifier to automatically listen and update the parent data property. (Vue documentation: .sync modifier)

<renderless-audio 
    src="/my-audio-file.mp3" 
    :muted.sync="noSound">
</renderless-audio>
export default {
    ...
    data() {
        return {
            /* will be passed as a prop but updates itself 
               if 'muted' state inside component changes */
            noSound: true
        }
    }
    ...
}

Slot

All your markup goes into the default slot.

<renderless-audio src="/my-audio-file.mp3">
    <template slot-scope="{ controls, time, state }">

        <!-- Default slot: Your custom markup -->

    </template>    
</renderless-audio>

The slot scope exposes objects controls, time, state and el containing all necessary info and methods to control playback:

controls

Method | Description --- | --- controls.play() | Starts playback controls.pause() | Pauses playback controls.stop() | Pauses playback and sets the position back to the beginning controls.togglePlay() | Starts or pauses playback, depending on the current state controls.mute() | Mutes audio controls.unmute() | Unmutes audio controls.toggleMuted() | Mutes or unmutes audio, depending on the current state controls.setVolume(volume) | Sets the volume (volume: floating number between 0 and 1) controls.seek(progress) | Sets playback position to the given percentage value (progress: Number between 0 and 100) controls.toggleLooping() | Activates or deactivates looping of the audio, depending on the current state controls.setPlaybackRate(rate) | Sets playback rate controls.reload() | Stops playback and reloads the audio file controls.download() | Opens/downloads the audio source file

time

Property / Method | Type | Description --- | --- | --- time.played | Number | The time played in seconds time.remaining | Number | The time remaining in seconds time.duration | Number | The total duration of the audio file in seconds time.format(seconds) | Function | Helper function to convert seconds into a human readable format (hh:mm:ss)

state

Property | Type | Description --- | --- | --- state.isReady | Boolean | Is true if player is ready for playback (first frames loaded) state.isWaiting | Boolean | Is true if player is buffering and waiting for data state.isMuted | Boolean | Is true if player is muted state.isLooping | Boolean | Is true if looping is active state.isLoaded | Boolean | Is true if the metadata is loaded state.hasEnded | Boolean | Is true if playback has reached the end and is not looping state.progress | Number | Current progress of playback in percent state.volume | Number | Current volume state.playbackRate | Number | Current playback speed state.buffered | Array | An array of all buffered timeranges, containing info about start, end and width (length) of buffered range. All numbers are percentages. state.currentSource| String| Currently loaded source file

el

The el object allows direct acces to the <audio> element.

Events

All native DOM events (Media events) of the underlying <audio> element are passed to the root of the component. Which means you can listen for native events directly on the <renderless-audio> component (Without the .native modifier!).

<renderless-audio
    src="/my-audio-file.mp3"
    @loadeddata="doSomething()"
    @canplaythrough="doSomethingElse()">
    ...
</renderless-audio>

Additional events In addition to the native media events, the component emits a few more useful events. Event | Data | Description --- | --- | --- init| Object | Fires when component is mounted and has been initialized. source-changed| Object | Fires when the source file changed. (Also passes the new source as source to the listener) stopped| Object | Fires when playback has stopped after calling controls.stop() looped| Object | Fires when playback has reached the end and looping is active

All of these events pass an object with (at least) the target and controls fields to the listener:

{
    // The `<audio>` element itself
    target: HTMLAudioElement

    // All functions described in the slot-scope 'controls' section
    controls: Array<Function>

    ...
}

This can be very useful if you want to directly react to certain events. In the following example we're immediately starting playback after the source has changed and muting the sound after the first loop:

<renderless-audio
    :src="myDynamicSource"
    @source-changed="$event.controls.play()"
    @looped="$event.controls.mute()">
    ...
</renderless-audio>

Prop update events You can listen to prop update events with @update:propName ('propName' being the actual name of the prop) or simply use the .sync modifier. (See: Sync props back to parent)

Access player controls from outside

Usually, all method calls to the player should happen inside your markup in the default slot. Although not really recommended, there are a few ways to call the control methods from outside:

Control methods are passed on the init event

<template>
    <renderless-audio
        @init="playerControls = $event.controls"
        src="/some-audio-file.mp3">
    </renderless-audio>

    <button @click="someActionOnParent()">Button outside slot</button>
</template>
<script>
export default {
    ...

    data() {
        return {
            playerControls: null
        }
    }

    methods: {
        someActionOnParent() {
            this.playerControls.seek(25)
            this.playerControls.play()
        }
    }

    ...
}
</script>

or

Make a reference to the component

<template>
    <renderless-audio
        ref="audioPlayer"
        src="/some-audio-file.mp3">
    </renderless-audio>

    <button @click="someActionOnParent()">Button outside slot</button>
</template>
<script>
export default {
    ...

    methods: {
        someActionOnParent() {
            this.$refs.audioPlayer.seek(25)
            this.$refs.audioPlayer.play()
        }
    }

    ...
}
</script>

Browser Support

This component has not been properly tested yet, but I'm planning to target all browsers supporting Vue 2.x and the <audio> element.

License

MIT