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.

–:–
Uploading
–:–
<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 CloseButton labelled Remove {name}; the error glyph carries an Upload failed label.

Props

PropTypeDefaultDescription
srcstringResolved 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.
namestringAccessible name / filename for the clip.
loadingbooleanfalseBusy state on the play control — e.g. a lazy fetch in flight.
onActivate() => voidLazy-load trigger — play pressed with no src yet.
status"uploading" | "ready" | "error"uploading shows a spinner; error a warning glyph.
onRemove() => voidRenders a remove control (composer / pending use).
classNamestringMerged onto the root (defaults to w-full max-w-sm).

On this page