# Audio Recorder

Enable audio recording on `MessageComposer` to let users record voice messages.

```tsx
import { MessageComposer } from "stream-chat-react";

const Composer = () => <MessageComposer audioRecordingEnabled />;
```

Once enabled, the composer renders `StartRecordingAudioButton`. When recording starts, `MessageComposerUI` swaps the normal composer UI for `AudioRecorder`.

## Best Practices

- Enable recording only where voice messages make sense for the product.
- Use `asyncMessagesMultiSendEnabled` if users should be able to combine text, attachments, and voice recordings before sending.
- Customize recording UI through `WithComponents`, not dead `Channel` override props.
- Keep permission-denied messaging short and actionable.
- Test both immediate-send and stacked-send behavior when changing recording flows.

## UI Customization

The default recording surfaces are provided through `ComponentContext`:

- `StartRecordingAudioButton`
- `AudioRecorder`
- `RecordingPermissionDeniedNotification`

Use `WithComponents` to override them:

```tsx
import { Channel, MessageComposer, WithComponents } from "stream-chat-react";

const CustomStartRecordingAudioButton = (props) => (
  <button {...props}>Record</button>
);

const CustomAudioRecorder = () => <div>Custom recorder UI</div>;
const CustomRecordingPermissionDeniedNotification = () => (
  <div>Allow microphone access in your browser settings.</div>
);

const App = () => (
  <WithComponents
    overrides={{
      AudioRecorder: CustomAudioRecorder,
      RecordingPermissionDeniedNotification:
        CustomRecordingPermissionDeniedNotification,
      StartRecordingAudioButton: CustomStartRecordingAudioButton,
    }}
  >
    <Channel>
      <MessageComposer audioRecordingEnabled />
    </Channel>
  </WithComponents>
);
```

The custom components above are app-owned examples.

## Browser Permissions

If microphone permission is denied when the user tries to start recording, the SDK opens a callout anchored to the recording button and renders `RecordingPermissionDeniedNotification`.

The default notification component receives only:

| Prop             | Type                       |
| ---------------- | -------------------------- |
| `permissionName` | `"microphone" \| "camera"` |

The close behavior is managed by the SDK dialog layer. Custom notification components do not receive an `onClose` callback.

## Custom Encoding

By default, recordings are encoded as `audio/wav`. To reduce size, you can use the MP3 encoder plugin based on [`lamejs`](https://github.com/gideonstele/lamejs):

1. Install `@breezystack/lamejs`.

```shell
npm install @breezystack/lamejs
```

```shell
yarn add @breezystack/lamejs
```

2. Pass the encoder through `audioRecordingConfig`.

```tsx
import { useMemo } from "react";
import { MessageComposer } from "stream-chat-react";
import { encodeToMp3 } from "stream-chat-react/mp3-encoder";

const Composer = () => {
  const audioRecordingConfig = useMemo(
    () => ({ transcoderConfig: { encoder: encodeToMp3 } }),
    [],
  );

  return (
    <MessageComposer
      audioRecordingConfig={audioRecordingConfig}
      audioRecordingEnabled
    />
  );
};
```

## Sending Behavior

The recording uploads when recording is completed.

Sending behavior is controlled by `asyncMessagesMultiSendEnabled` on `MessageComposer`:

| `asyncMessagesMultiSendEnabled` | Behavior                                                                                             |
| ------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `false`                         | uploads and sends the voice recording immediately                                                    |
| `true`                          | keeps the voice recording in the composer preview stack until the user submits the whole composition |

When a recording stays in the composer, it is rendered in the dedicated `VoiceRecordingPreviewSlot`.

## Recording Controller

Components consuming `MessageComposerContext` can read recorder state through `recordingController`:

```tsx
import { useMessageComposerContext } from "stream-chat-react";

const RecorderStatus = () => {
  const {
    recordingController: {
      completeRecording,
      permissionState,
      recorder,
      recording,
      recordingState,
    },
  } = useMessageComposerContext();

  return (
    <pre>
      {JSON.stringify(
        {
          hasRecorder: !!recorder,
          permissionState,
          recordingState,
          hasRecording: !!recording,
        },
        null,
        2,
      )}
    </pre>
  );
};
```

The controller exposes:

| Property            | Description                                        |
| ------------------- | -------------------------------------------------- |
| `completeRecording` | stops recording and finalizes the voice attachment |
| `permissionState`   | current browser permission state                   |
| `recorder`          | media-recorder controller instance                 |
| `recording`         | generated voice-recording attachment after stop    |
| `recordingState`    | current recording lifecycle state                  |


---

This page was last updated at 2026-04-13T07:26:59.253Z.

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v14/components/message-composer/audio-recorder/](https://getstream.io/chat/docs/sdk/react/v14/components/message-composer/audio-recorder/).