An audio player is the rare interface where the browser already ships something correct. <audio controls> gives you play, pause, a scrubber, volume, a keyboard path and a screen reader announcement, in one line, styled by the platform. Everything after that is a decision to trade all of it for a look.
Sometimes that trade is right. Here is what you are taking on when you make it.
The three shapes
The bar. A play button, elapsed time, a scrubber, duration. Audio Player by Chetan Verma is the compact version, and it is what a podcast page or an article with a spoken version needs.
The widget. Artwork, title, artist, transport controls, sometimes a queue. Music Player Widget by Syed Mohammad Ammar is the full one, with an audio-reactive dot-grid visualiser, and Music Toggle btn by reuno-ui is the minimal always-present control.
The waveform. A rendered picture of the audio, scrubable by clicking anywhere in it. Waveform by The Gridcn is the shape, and MusicEqualizer Upload by Ruixen UI pairs it with an upload.
For recording rather than playback, Audio Upload Card by Isaiah is the input side, and AI Voice Input by kokonutd is the voice-capture state that AI products need.




What you rebuild when you replace the native element
Six things, and the last two are the ones that get discovered in production.
Keyboard. Space to toggle, arrows to seek, and the control has to be focusable in the first place. A div with a click handler is none of that.
Announcements. The state, the position and the duration have to be exposed. A play button whose only signal is an icon swap says nothing to a screen reader; toggle the accessible name between "Play" and "Pause" rather than only the glyph.
Buffering. Between pressing play and hearing sound there is a wait, and it needs a visible state, or the reader presses play twice.
Errors. Files 404, formats are unsupported, networks drop mid-stream. The error event needs a visible message, not a silent dead button.
Autoplay is blocked, and should be. Browsers refuse to start audio without a user gesture, and they are right to. Design for the paused first state, and never build a flow that assumes sound has started.
Media Session. Two lines of navigator.mediaSession.metadata put your title and artwork on the lock screen and make the hardware play button work. It is the cheapest polish in this entire article and almost nobody ships it.
The waveform, honestly
Drawing a waveform means decoding the audio to get its samples, which is either done ahead of time on the server or in the browser with the Web Audio API. In the browser it means downloading the whole file before the picture appears, which is a real cost for a one-hour recording.
The usual production answer is to precompute a small array of peaks server-side and ship that as JSON. The waveform then renders instantly from a few kilobytes, and the audio streams normally behind it.
Where else to look
The honest list, because the answer is not always us:
| Source | Best for | Trade-off |
|---|---|---|
<audio controls> | Correct behaviour in one line, for free | The platform's look, not yours |
| wavesurfer.js | Waveforms, regions and precise scrubbing | A library, and the decode cost is yours to manage |
| Media Session API | Lock screen controls and hardware keys | Two lines, and it only complements a player |
| 21st | Bars, widgets, visualisers and voice capture | Quality varies by author, so preview before you take it |
Taking one
Every component page has a live preview and the code. Installing goes through the shadcn CLI against our registry:
That key comes from your 21st account, and installs require a membership. Set API_KEY_21ST once in your shell and the command works for anything in the catalogue. The 21st MCP covers the same ground without leaving the editor: ask Claude, Cursor or Codex for an audio player, get the previews inline, and let the agent write the file.
Frequently asked
- Should I use the native audio element?
- Unless the look is a requirement, yes. One line of markup gives you play, pause, scrubbing, volume, a keyboard path and screen reader announcements, styled by the platform. Replacing it means rebuilding all of that, and most custom players ship three of the six things it handles.
- What do custom audio players usually get wrong?
- Keyboard operation, since a div with a click handler is not focusable. Announcements, because swapping an icon says nothing: toggle the accessible name between Play and Pause. Buffering, so the reader does not press play twice. And error handling, since files 404 and formats are unsupported more often than local testing suggests.
- How do you show a waveform without downloading the whole file?
- Precompute the peaks on the server and ship them as a small JSON array. Decoding audio in the browser to draw a waveform means downloading the entire file first, which is a real cost for a long recording. With precomputed peaks the picture renders instantly from a few kilobytes while the audio streams normally.
- Why will my audio not autoplay?
- Browsers block audio that starts without a user gesture, deliberately. Design for a paused first state and never build a flow that assumes sound has already started. Adding Media Session metadata is the useful thing to do instead: it puts your title and artwork on the lock screen and makes hardware play buttons work.