import {
Attachment,
AttachmentProps,
VoiceRecordingPlayer,
VoiceRecordingProps,
Channel,
QuotedVoiceRecording,
} from 'stream-chat-react';
import { ChannelInner } from './ChannelInner';
const CustomVoiceRecording = ({ attachment, isQuoted }: VoiceRecordingProps) =>
isQuoted ? (
<QuotedVoiceRecording attachment={attachment} />
) : (
<VoiceRecordingPlayer attachment={attachment} playbackRates={[2.0, 3.0]} />
);
const CustomAttachment = (props: AttachmentProps) => (
<Attachment {...props} VoiceRecording={CustomVoiceRecording} />
);
const App = () => (
<Channel Attachment={CustomAttachment}>
<ChannelInner />
</Channel>
);
export default App;Voice Recording Attachment
Audio attachments recorded from the chat UI are called voice recordings. The SDK provides a default VoiceRecording implementation, which renders either VoiceRecordingPlayer or QuotedVoiceRecording.
VoiceRecordingPlayer appears in the attachment list and plays the audio.

QuotedVoiceRecording displays a compact summary in quoted replies.

Best Practices
- Preserve both
VoiceRecordingPlayerandQuotedVoiceRecordingfor clarity. - Keep custom playback rates minimal to avoid cluttered controls.
- Provide fallback UI for missing duration/title/waveform data.
- Use CSS tweaks for small display changes before replacing components.
- Test playback controls on mobile and low-bandwidth connections.
Attachment payload
Voice recording attachments include the following properties:

These properties serve the following purpose:
| Property | Description |
|---|---|
| asset_url | the URL where the voice recording is downloaded from |
| duration | the audio duration in seconds |
| file_size | the file size in bytes (displayed as fallback to duration if duration is not available) |
| mime_type | the file type that is later reflected in the icon displayed in the voice recording attachment widget |
| title | the audio title |
| type | the value will always be "voiceRecording" |
| waveform_data | the array of fractional number values between 0 and 1. These values represent the amplitudes later reflected in the WaveProgressBar |
VoiceRecordingPlayer
Navigation
Click the waveform or drag the progress indicator to seek. The indicator moves to the click/drag position and the preceding bars highlight to show progress.

Playback speed change
Change playback speed with PlaybackRateButton. It appears only during playback. Repeated clicks cycle rates; after the max rate, it resets to the initial value.

UI Fallbacks
Missing duration
If duration is missing from the attachment payload, both VoiceRecordingPlayer and QuotedVoiceRecording display file size instead.


Missing title
If the voice recording has no title, a fallback title is shown.


Missing waveform_data
If waveform_data is empty, no progress bar is rendered.

Default components customization
Customize the default VoiceRecording in two steps:
Create a custom voice recording component (e.g.
CustomVoiceRecording). It will serve as a wrapper component that rendersVoiceRecordingPlayerresp.QuotedVoiceRecording. Pass props to these components.Create a custom attachment component (e.g.
CustomAttachment), that will be again a wrapper around the SDK'sAttachmentcomponent. Pass the custom voice recording component to theAttachmentcomponent via propVoiceRecording.
Provide custom list of playback speeds
You can override the default list of playback rates by overriding the VoiceRecording component.
Example:
Remove title
This could be solved by customizing the styles. You can stop displaying the recording title by tweaking the CSS:
.str-chat__message-attachment__voice-recording-widget__title {
display: none;
}Customize the fallback title
If you do not like our fallback title, you can change it by changing the translation key "Voice message".
Other customizations
If you would like to perform the following customizations:
- change the progress bar
- change the file icon SVG
We recommend assembling your own components to display voice data and playback UI, then passing them into the custom attachment component above.
VoiceRecordingPlayer and QuotedVoiceRecording are small components, so the default implementations are good references.