Audio Attachment
An audio message or voice note — a compact AudioPlayer with an optional upload spinner, error glyph, and remove control, for chat composers and transcripts.
<AudioAttachment src="/voice-note.webm" name="voice-note.webm" />Usage
import { AudioAttachment } from "@reva/ui";AudioAttachment wraps a compact AudioPlayer with the chrome a chat surface needs: an optional upload spinner / error glyph and a remove control. It's presentation only — the consumer owns the data, exactly like AttachmentChip. Pass a resolved src, or wire onActivate + loading to fetch the bytes lazily on the first play press (so a transcript doesn't download every clip up front).
Examples
In the composer
A pending attachment is playable immediately (from the local blob) and removable. Pass status="uploading" while the upload is in flight; onRemove renders the close control.
<AudioAttachment src={previewUrl} name="voice-message.webm" status="uploading" onRemove={handleRemove}/>Status
status drives the trailing affordance — uploading shows a spinner, error a destructive warning glyph. The clip stays playable in both.
<AudioAttachment src={url} name="uploading.webm" status="uploading" onRemove={remove} /><AudioAttachment src={url} name="failed.webm" status="error" onRemove={remove} />Lazy loading in a transcript
For a received message, fetch on demand: leave src empty and pass onActivate (the play press) + loading. Once the bytes resolve, playback starts automatically.
function ChatAudioAttachment({ filename, documentId }: { filename: string; documentId?: string }) {
const [activated, setActivated] = useState(false);
const [attempt, setAttempt] = useState(0);
const preview = useDocumentPreview(documentId, activated, attempt);
return (
<AudioAttachment
name={filename}
src={preview.objectUrl}
loading={activated && preview.status === "loading"}
// Bump a reload key on a play-after-error press so the fetch retries.
onActivate={() => {
if (preview.status === "error") setAttempt((n) => n + 1);
setActivated(true);
}}
/>
);
}Accessibility
- Inherits the AudioPlayer transport semantics (labelled
role="group", keyboard-operable controls). - The remove control is a
CloseButtonlabelledRemove {name}; the error glyph carries anUpload failedlabel.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | — | Resolved audio URL — object URL, remote URL, or data URL. |
data | { base64: string; mediaType?: string } | — | AI SDK-style base64 speech result, used when src is absent. |
name | string | — | Accessible name / filename for the clip. |
loading | boolean | false | Busy state on the play control — e.g. a lazy fetch in flight. |
onActivate | () => void | — | Lazy-load trigger — play pressed with no src yet. |
status | "uploading" | "ready" | "error" | — | uploading shows a spinner; error a warning glyph. |
onRemove | () => void | — | Renders a remove control (composer / pending use). |
className | string | — | Merged onto the root (defaults to w-full max-w-sm). |