# Audio Recorder

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

`AudioRecorder` is the recorder that `MessageComposer` shows in place of its input while a voice recording runs. `StartRecordingAudioButton` starts the recording. Both appear only on a composer with `audioRecordingEnabled`. [Voice recordings and audio playback](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/voice-recordings-and-audio-playback/#recording) explains how a recording is made and sent.

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

export const ComposerWithRecording = () => (
  <MessageComposer audioRecordingEnabled />
);
```

## Best practices

- Turn recording on for each composer that should have it. A thread's `MessageComposer` needs `audioRecordingEnabled` too.
- Declare `audioRecordingConfig` once, at module scope or in `useMemo`. A new object on every render creates a new recorder on every render.
- Replace the recorder's parts through their override keys. A part of your own reads the recorder from `recordingController` in `useMessageComposerContext()`.
- Forward the `ref` in a `StartRecordingAudioButton` of your own: the permission callout is anchored to it.
- Serve the app over HTTPS. Browsers offer `navigator.mediaDevices` only on HTTPS and `localhost`, and without it the record button doesn't show.

## UI customization

| Override key                            | Replaces                                                                      | Props                                                                                                                                               |
| --------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `StartRecordingAudioButton`             | The record button                                                             | `StartRecordingAudioButtonProps`, the `button` props                                                                                                |
| `AudioRecorder`                         | The recorder, while a recording runs                                          | none                                                                                                                                                |
| `RecordingPermissionDeniedNotification` | The callout shown when the microphone is blocked                              | `RecordingPermissionDeniedNotificationProps`                                                                                                        |
| `VoiceRecordingPreviewSlot`             | A finished recording waiting to be sent, with `asyncMessagesMultiSendEnabled` | see [Composer UI components](https://getstream.io/chat/docs/sdk/react/v15/component-reference/message-composer/composer-ui-components/#voicerecordingpreviewslot-props) |

The composer shows the record button in place of the send button while the composer is empty, nothing is being edited, no command is active, no slow mode cooldown runs and the browser can record. The button gets `onClick` and `disabled`: it's disabled while a recording runs and, without `asyncMessagesMultiSendEnabled`, while the composer already holds a voice recording.

The default `AudioRecorder` shows a live waveform and a timer while recording, and a player for what was recorded while it's paused. Its buttons pause and resume, and complete the recording. While it's paused, a delete button cancels the recording and publishes a "Voice message deleted" notification.

```tsx
import { forwardRef } from "react";
import type { StartRecordingAudioButtonProps } from "stream-chat-react";
import { MessageComposer, WithComponents } from "stream-chat-react";

const RecordButton = forwardRef<
  HTMLButtonElement,
  StartRecordingAudioButtonProps
>(function RecordButton(props, ref) {
  return (
    <button {...props} className="record-button" ref={ref} type="button">
      Record
    </button>
  );
});

const overrides = { StartRecordingAudioButton: RecordButton };

export const ComposerWithRecordButton = () => (
  <WithComponents overrides={overrides}>
    <MessageComposer audioRecordingEnabled />
  </WithComponents>
);
```

[The recorder](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/voice-recordings-and-audio-playback/#the-recorder) replaces `AudioRecorder` with a recorder of its own.

## Browser permissions

The first click on the record button starts the recorder, and the browser asks for the microphone. When access was denied, the click opens a callout anchored to the button that renders `RecordingPermissionDeniedNotification`: "Allow access to microphone" and how to do it.

| Prop             | Type                  | Default  | Description                                                                                |
| ---------------- | --------------------- | -------- | ------------------------------------------------------------------------------------------ |
| `permissionName` | `RecordingPermission` | required | The blocked permission: `"microphone"`, or `"camera"`. The composer passes `"microphone"`. |

The SDK's dialog layer closes the callout, so the notification gets no `onClose`. Its text comes from the translation keys under `mediaRecorder.permissionDenied`.

`recordingController.permissionState` holds the browser's answer, `"granted"`, `"denied"` or `"prompt"`, for a part that wants to show it before the click.

## Custom encoding

`audioRecordingConfig` on `MessageComposer` configures the recorder. It's a `CustomAudioRecordingConfig`, with three parts, each optional:

| Field                     | Holds                                                                                                                                                                                        | Default                                                                                                                   |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `mediaRecorderConfig`     | The browser `MediaRecorder` options, such as `mimeType`                                                                                                                                      | `mimeType`: `"audio/webm"`, or `"audio/mp4;codecs=mp4a.40.2"` where WebM can't be recorded                                |
| `transcoderConfig`        | `sampleRate`, and `encoder: (file: File, sampleRate: number) => Promise<Blob>`, which converts the recording                                                                                 | `sampleRate`: `16000`; the encoder produces WAV                                                                           |
| `amplitudeRecorderConfig` | The waveform: `sampleCount`, how many values are saved; `samplingFrequencyMs`, how often the level is read; `analyserConfig`, the `fftSize`, `minDecibels` and `maxDecibels` of the analyser | `sampleCount`: `100`; `samplingFrequencyMs`: `60`; `analyserConfig`: `{ fftSize: 32, maxDecibels: 0, minDecibels: -100 }` |

A recording made as `audio/mp4` isn't converted; every other recording goes through the encoder. To send MP3 instead of WAV, pass `encodeToMp3` from `stream-chat-react/mp3-encoder`, which needs the `@breezystack/lamejs` package:

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

// Declared once: a new object on every render creates a new recorder.
const audioRecordingConfig: CustomAudioRecordingConfig = {
  amplitudeRecorderConfig: { sampleCount: 60 },
  transcoderConfig: { encoder: encodeToMp3 },
};

export const Mp3Composer = () => (
  <MessageComposer
    audioRecordingConfig={audioRecordingConfig}
    audioRecordingEnabled
  />
);
```

[Encoding recordings as MP3](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/voice-recordings-and-audio-playback/#encoding-recordings-as-mp3) has the details.

## Sending behavior

Completing a recording turns it into a `voiceRecording` attachment and uploads it with the composer's attachment manager. What happens next depends on `asyncMessagesMultiSendEnabled` on `MessageComposer`:

| `asyncMessagesMultiSendEnabled` | After the upload                                                                                                                          |
| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `false`, the default            | The composer sends the message at once.                                                                                                   |
| `true`                          | The recording stays in the composer, shown by `VoiceRecordingPreviewSlot`, and goes out with the rest of the message when the user sends. |

The attachment's fields are on [Voice Recording Attachment](https://getstream.io/chat/docs/sdk/react/v15/component-reference/voice-recording/voice-recording-attachment/#attachment-payload).

## Recording controller

Parts inside `MessageComposer` read the recorder from `recordingController` in `useMessageComposerContext()`. This badge shows while a recording runs:

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

export const RecordingBadge = () => {
  const { recordingController } = useMessageComposerContext();

  if (recordingController.recordingState !== MediaRecordingState.RECORDING) {
    return null;
  }

  return <span className="recording-badge">Recording…</span>;
};
```

`recordingController` is a `RecordingController`:

| Field               | Type                                         | Description                                                                                                                                                                       |
| ------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `recorder`          | `MediaRecorderController \| undefined`       | The recorder: the `MediaRecorderController` methods `start()`, `pause()`, `resume()`, `stop()` and `cancel()` control the recording. `undefined` without `audioRecordingEnabled`. |
| `recordingState`    | `MediaRecordingState \| undefined`           | `RECORDING`, `PAUSED` or `STOPPED`, or `undefined` while nothing is being recorded.                                                                                               |
| `recording`         | `LocalVoiceRecordingAttachment \| undefined` | The recording as an attachment, once the recorder is paused or stopped.                                                                                                           |
| `permissionState`   | `PermissionState \| undefined`               | The browser's microphone permission.                                                                                                                                              |
| `completeRecording` | `() => void`                                 | Stops the recorder, uploads the recording and, without `asyncMessagesMultiSendEnabled`, sends it.                                                                                 |

`MediaRecordingState` and `RecordingPermission` are exported enums, and `RecordingController` and `CustomAudioRecordingConfig` are exported types.

## Related hooks

| Hook                             | Returns                                                                                  |
| -------------------------------- | ---------------------------------------------------------------------------------------- |
| `useMessageComposerContext()`    | the composer's props and `recordingController`; throws outside `MessageComposer`         |
| `useMessageComposerController()` | the composer the recording is added to: the channel's, the thread's or the edit composer |

See [useMessageComposerContext](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/composer-hooks/#usemessagecomposercontext).

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/component-reference/voice-recording/audio-recorder/](https://getstream.io/chat/docs/sdk/react/v15/component-reference/voice-recording/audio-recorder/).