Voice Message Bubble
wa-ui/voice-message-bubbleFreeComponent
WhatsApp-style voice message with play/pause, waveform, playback progress, duration, and read receipts.
Install it
npx shadcn@latest add https://ui.meta-cloud-api.site/r/voice-message-bubble.jsonSource
1"use client";23import * as React from "react";4import { cn } from "@/lib/utils";5import "@/components/ui/whatsapp/styles/whatsapp.css";6import { type MessageStatus, MessageStatusIcon } from "./message-status";78interface VoiceMessageBubbleProps extends React.HTMLAttributes<HTMLDivElement> {9 variant?: "incoming" | "outgoing";10 duration: string;11 timestamp?: string;12 status?: MessageStatus;13 showTail?: boolean;14 /** When provided, the component manages playback internally */15 audioSrc?: string;16 /** Avatar image shown on the left when playing (incoming) */17 avatarSrc?: string;18 /** Controlled mode — ignored when audioSrc is set */19 isPlaying?: boolean;20 progress?: number;21 onPlayPause?: () => void;22}2324// Deterministic fake waveform bars25const WAVEFORM_BARS = [3, 5, 8, 6, 10, 7, 4, 9, 6, 5, 8, 10, 7, 4, 6, 9, 5, 8, 3, 6, 10, 7, 5, 8, 4, 9, 6, 7, 5, 8];2627function PlayIcon({ className }: { className?: string }) {28 return (29 <svg viewBox="0 0 24 24" width="24" height="24" className={className}>30 <path d="M8 5v14l11-7z" fill="currentColor" />31 </svg>32 );33}3435function PauseIcon({ className }: { className?: string }) {36 return (37 <svg viewBox="0 0 24 24" width="24" height="24" className={className}>38 <path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z" fill="currentColor" />39 </svg>40 );41}424344const VoiceMessageBubble = React.forwardRef<HTMLDivElement, VoiceMessageBubbleProps>(45 (46 {47 className,48 variant = "incoming",49 duration,50 timestamp,51 status,52 showTail = false,53 audioSrc,54 avatarSrc,55 isPlaying: isPlayingProp = false,56 progress: progressProp = 0,57 onPlayPause,58 ...props59 },60 ref61 ) => {62 const isOutgoing = variant === "outgoing";63 const audioRef = React.useRef<HTMLAudioElement>(null);64 const [internalPlaying, setInternalPlaying] = React.useState(false);65 const [internalProgress, setInternalProgress] = React.useState(0);66 const [internalDuration, setInternalDuration] = React.useState(duration);6768 // Use internal state when audioSrc provided, otherwise use props69 const isPlaying = audioSrc ? internalPlaying : isPlayingProp;70 const progress = audioSrc ? internalProgress : progressProp;7172 const handlePlayPause = () => {73 if (!audioSrc) { onPlayPause?.(); return; }74 const audio = audioRef.current;75 if (!audio) return;76 if (internalPlaying) { audio.pause(); } else { audio.play(); }77 };7879// … truncated
Files it writes
More from wa-ui
All 29 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Action Buttonaction-button | wa-ui | Components | Free | 1 dep · 2 files | |
| Call Permissioncall-permission | wa-ui | Components | Free | 1 dep · 2 files | |
| Carousel Templatecarousel-template | wa-ui | Components | Free | 1 dep · 3 files | |
| Chat Bubblechat-bubble | wa-ui | Components | Free | no deps · 2 files | |
| Chat Headerchat-header | wa-ui | Components | Free | no deps · 2 files | |
| Chat List Itemchat-list-item | wa-ui | Components | Free | no deps · 2 files | |
| Chat Menuchat-menu | wa-ui | Components | Free | 1 dep · 2 files | |
| Contact Bubblecontact-bubble | wa-ui | Components | Free | 1 dep · 3 files |
