A voice message bar with a scrubbable waveform and a play control.
Press play to start. The play triangle splits into the pause bars, the waveform fills from the left, and soft lights circle the inside edge of the bar. Pause and they wind down to a stop. Drag across the waveform to scrub, or focus it and use the arrow keys. Press the time to change playback speed.
Options you can pass to customize this component.
srcstringURL of the audio file to play. Without it the bar runs on a timer, which is what the demo above does.
durationnumberLength of the clip in seconds. Ignored once a real file reports its own duration.
waveformnumber[]Bar heights from 0 to 1. Leave it out and the bars are generated from the seed.
barsnumberHow many bars to generate. They share the width evenly, so more bars means thinner bars.
seednumberPicks which generated waveform you get. The same seed always draws the same bars.
playingbooleanDrives playback yourself. Leave it out and the bar tracks its own state.
defaultPlayingbooleanWhether the bar starts playing when you are not driving it.
onPlayingChange(playing: boolean) => voidCalled whenever playback starts or stops.
onEnded() => voidCalled when the clip reaches the end, just after the bar rewinds to the start.
accent#FC4C01#1A73F2#4ADE80Any hex color for the light behind the bar. The waveform itself stays neutral.
sizesmmdlgBar size. Height, control, bar width and text all scale together.
speedsnumber[]Speeds the time label cycles through when pressed. Pass a single speed to pin it and turn the label back into plain text.
onSpeedChange(speed: number) => voidCalled with the new speed each time the label is pressed.
seekablebooleanWhether the waveform can be scrubbed. The arrow keys move 5 seconds at a time, Home and End jump to the ends. Off removes both the pointer and the keyboard seeking.
classNamestringExtra classes merged onto the root element (data-slot="voice-note"). Set the width here.
childrenReactNodeOn VoiceNoteGroup only. Wrap several bars in it and starting one stops the rest, the way a chat thread behaves.
npx shadcn@latest add swamimalode07/rare-ui/voice-note"use client"import { VoiceNote } from "@/components/ui/voice-note"export function Demo() {return <VoiceNote src="/audio/note.mp3" className="w-[340px]" />}// no file yet: pass a length and it runs on a timer// <VoiceNote duration={53} accent="#2563EB" />// wrap a thread in VoiceNoteGroup and starting one stops the rest// <VoiceNoteGroup>// <VoiceNote src="/audio/first.mp3" />// <VoiceNote src="/audio/second.mp3" />// </VoiceNoteGroup>
Click the code icon in the top-right corner to view the source code.
Most components here are recreations of great work from around the web. I don't claim to be the original creator - this is my attempt to reverse-engineer, replicate, and often add a few extra features. I've tried to credit everyone; if I missed someone, let me know.
1