> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daily.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Recording

> Use startRecording(), stopRecording(), and updateRecording() to control Daily call recordings from daily-js.

<Card title="Recording overview" icon="video" href="/docs/guides/features/recording">
  New to Daily recording? Start here — covers recording types, when to use each, how to enable recording on a room or meeting token, and how to retrieve recordings.
</Card>

Recording must be enabled on the room or meeting token (via `enable_recording`) before `startRecording()` will work. See the overview guide above for setup.

## Starting a recording

[`startRecording()`](/reference/daily-js/instance-methods/start-recording) begins a recording with optional configuration:

```javascript theme={null}
// Start a cloud recording with defaults
call.startRecording();

// Start with explicit options
call.startRecording({
  type: 'cloud',
  width: 1920,
  height: 1080,
  fps: 30,
  videoBitrate: 4000,
  audioBitrate: 128,
  backgroundColor: '#000000',
});
```

## Recording options

<ParamField path="type" type="'cloud' | 'raw-tracks' | 'local' | 'cloud-audio-only'">
  The recording mode. Defaults to whichever type is enabled on the room or token. See the [recording overview](/docs/guides/features/recording) for a description of each type and when to use it.
</ParamField>

<ParamField path="width" type="number">
  Output video width in pixels. Default: `1280`.
</ParamField>

<ParamField path="height" type="number">
  Output video height in pixels. Default: `720`.
</ParamField>

<ParamField path="fps" type="number">
  Frames per second. Default: `30`.
</ParamField>

<ParamField path="videoBitrate" type="number">
  Video bitrate in kbps.
</ParamField>

<ParamField path="audioBitrate" type="number">
  Audio bitrate in kbps.
</ParamField>

<ParamField path="minIdleTimeOut" type="number">
  Seconds of idle time (no active streams) before the recording automatically
  stops. Default: `60`.
</ParamField>

<ParamField path="maxDuration" type="number">
  Maximum recording duration in seconds.
</ParamField>

<ParamField path="backgroundColor" type="string">
  Background color for the composed output when participant tiles do not fill
  the frame. Accepts a CSS hex color string, e.g. `'#1a1a2e'`.
</ParamField>

<ParamField path="instanceId" type="string">
  Identifier for this recording instance. Required when running multiple
  simultaneous recordings. See [multiple simultaneous recordings](#multiple-simultaneous-recordings).
</ParamField>

<ParamField path="layout" type="DailyStreamingLayoutConfig">
  Controls how participant tiles are arranged in the composed output. See [`DailyStreamingLayoutConfig`](/reference/daily-js/types/daily-streaming-layout-config) for the full type reference, or [layout presets](#layout-presets) below for usage examples.
</ParamField>

## Layout presets

The `layout` option controls the visual composition of cloud recordings. See [`DailyStreamingLayoutConfig`](/reference/daily-js/types/daily-streaming-layout-config) for field-level documentation on each preset. For screenshots, see the [recording overview](/docs/guides/features/recording#customize-cloud-recording-layouts).

### `default`

```javascript theme={null}
call.startRecording({
  layout: {
    preset: 'default',
    max_cam_streams: 9,         // maximum cameras shown
    participants: {
      video: ['session-id-1'],  // specific participant always shown
      sort: 'active',           // sort by speaking activity
    },
  },
});
```

### `single-participant`

```javascript theme={null}
call.startRecording({
  layout: {
    preset: 'single-participant',
    session_id: 'abc123',  // required: which participant to show
  },
});
```

### `active-participant`

```javascript theme={null}
call.startRecording({
  layout: { preset: 'active-participant' },
});
```

### `portrait`

```javascript theme={null}
call.startRecording({
  layout: {
    preset: 'portrait',
    variant: 'inset',  // 'vertical' (default) or 'inset'
    max_cam_streams: 4,
  },
});
```

### `audio-only`

```javascript theme={null}
call.startRecording({
  type: 'cloud-audio-only',
  layout: { preset: 'audio-only' },
});
```

### `custom`

Use Daily's VCS baseline composition to fully control the layout programmatically — modes, overlays, labels, participant ordering, and more. The [`startRecording()` reference](/reference/daily-js/types/daily-streaming-layout-config#baseline-composition-properties) documents all available `composition_params`.

```javascript theme={null}
call.startRecording({
  layout: {
    preset: 'custom',
    composition_id: 'daily:baseline',
    composition_params: {
      mode: 'dominant',
      'videoSettings.showParticipantLabels': true,
    },
    session_assets: {
      'images/logo': 'https://example.com/logo.png',
    },
  },
});
```

### `participants`

Several presets accept a `participants` field to filter which participants' video and audio are included in the recording. See [`DailyStreamingLayoutConfig`](/reference/daily-js/types/daily-streaming-layout-config#participants) for the full field list.

<Note>
  You must resend `participants` on every `updateRecording()` call — it is not persisted from the previous call. If omitted on update, all participants will be included.
</Note>

## Updating layout during recording

Call [`updateRecording()`](/reference/daily-js/instance-methods/update-recording) to switch layouts while a recording is in progress, without stopping and restarting:

```javascript theme={null}
// Start in default grid layout
call.startRecording({
  layout: { preset: 'default' },
});

// Later, switch to follow the active speaker
call.updateRecording({
  layout: { preset: 'active-participant' },
});
```

## Stopping a recording

[`stopRecording()`](/reference/daily-js/instance-methods/stop-recording) ends the active recording:

```javascript theme={null}
call.stopRecording();
```

## Multiple simultaneous recordings

Pass a unique `instanceId` to run more than one recording at the same time — for example, two cloud recordings with different layouts for different audiences:

```javascript theme={null}
// Portrait layout for mobile
call.startRecording({
  instanceId: 'portrait',
  type: 'cloud',
  layout: { preset: 'portrait', variant: 'vertical' },
});

// Landscape layout for desktop
call.startRecording({
  instanceId: 'landscape',
  type: 'cloud',
  layout: { preset: 'active-participant' },
});

// Update and stop each independently
call.updateRecording({ instanceId: 'landscape', layout: { preset: 'default' } });
call.stopRecording({ instanceId: 'portrait' });
call.stopRecording({ instanceId: 'landscape' });
```

<Note>
  Multiple instances work best when each runs the same recording type. Mixing types (e.g., `cloud` + `raw-tracks` simultaneously) is possible but not a recommended pattern — if you need both, contact [Daily support](https://www.daily.co/contact/support) to discuss your use case. For full details on instance limits and billing, see the [multi-instance guide](/docs/guides/features/live-streaming/multi-instance).
</Note>

## Recording events

Full payload details for all recording events are in the [recording events reference](/reference/daily-js/events/recording-events).

### `recording-started`

```javascript theme={null}
call.on('recording-started', (event) => {
  console.log('Recording started:', event.recordingId);
  console.log('Type:', event.type);           // 'cloud' | 'raw-tracks' | ...
  console.log('Started by:', event.startedBy); // session ID
  console.log('Layout:', event.layout);
});
```

### `recording-stopped`

```javascript theme={null}
call.on('recording-stopped', () => {
  console.log('Recording stopped.');
});
```

### `recording-error`

```javascript theme={null}
call.on('recording-error', ({ errorMsg }) => {
  console.error('Recording error:', errorMsg);
});
```

## Complete cloud recording example

```javascript theme={null}
const call = Daily.createCallObject();
await call.join({ url: 'https://your-domain.daily.co/room' });

// Start a 1080p cloud recording with active-speaker layout
call.startRecording({
  type: 'cloud',
  width: 1920,
  height: 1080,
  fps: 30,
  layout: { preset: 'active-participant' },
});

call.on('recording-started', ({ recordingId }) => {
  console.log('Recording ID:', recordingId);
  recordingBadge.hidden = false;
});

call.on('recording-stopped', () => {
  recordingBadge.hidden = true;
});

call.on('recording-error', ({ errorMsg }) => {
  console.error('Recording failed:', errorMsg);
});

// Switch layout mid-recording when a screen share starts
call.on('local-screen-share-started', () => {
  call.updateRecording({
    layout: {
      preset: 'single-participant',
      session_id: call.participants().local.session_id,
    },
  });
});

call.on('local-screen-share-stopped', () => {
  call.updateRecording({
    layout: { preset: 'active-participant' },
  });
});

document.getElementById('stop-recording').onclick = () =>
  call.stopRecording();
```
