> ## 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.

# Cameras, mics, and speakers

> Enumerate, select, and toggle media devices in React with useDevices, and handle permission and device errors.

[`useDevices`](/reference/daily-react/use-devices) is the single hook for everything about a user's media devices: the lists of cameras, microphones, and speakers, which one is selected, the permission state, and helpers to switch devices. Toggling tracks on and off is a separate, imperative action on the call object.

## Read available devices

`useDevices` returns `cameras`, `microphones`, and `speakers` arrays, plus the currently selected device for each:

```jsx theme={null}
import { useDevices } from '@daily-co/daily-react';

function DeviceLists() {
  const { cameras, microphones, speakers, currentCam, currentMic } = useDevices();
  return (
    <div>
      <p>Camera: {currentCam?.device.label}</p>
      <p>Mic: {currentMic?.device.label}</p>
      <p>{cameras.length} cameras, {microphones.length} mics, {speakers.length} speakers</p>
    </div>
  );
}
```

Each device item has a `device` (the browser [`MediaDeviceInfo`](https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo)), a `selected` boolean, and a `state` of `'granted'` or `'in-use'`.

## Build a device picker

Use `setCamera`, `setMicrophone`, and `setSpeaker` (each takes a `deviceId`) to switch devices:

```jsx theme={null}
import { useDevices } from '@daily-co/daily-react';

function CameraPicker() {
  const { cameras, setCamera } = useDevices();
  return (
    <select onChange={(e) => setCamera(e.target.value)}>
      {cameras.map(({ device }) => (
        <option key={device.deviceId} value={device.deviceId}>
          {device.label}
        </option>
      ))}
    </select>
  );
}
```

If devices change while your app is open (a headset is plugged in, for example), call `refreshDevices()` to re-enumerate.

## Handle permission and device state

`camState` and `micState` describe access at a glance. They start as `'idle'` before any access is requested, move to `'pending'` while the browser prompt is open, then settle on `'granted'` or `'blocked'`:

```jsx theme={null}
import { useDevices } from '@daily-co/daily-react';

function CameraGate() {
  const { camState, hasCamError, cameraError } = useDevices();

  if (camState === 'blocked') return <p>Camera access is blocked. Check your browser settings.</p>;
  if (camState === 'in-use') return <p>Your camera is in use by another app.</p>;
  if (hasCamError) return <p>Camera error: {cameraError?.msg}</p>;
  if (camState === 'pending') return <p>Allow camera access to continue…</p>;

  return <CameraControls />;
}
```

The convenience booleans `hasCamError` and `hasMicError` are `true` whenever the corresponding state is an error state (`'blocked'`, `'in-use'`, `'not-found'`, and similar), so you can branch on one value instead of enumerating them.

<Note>
  States stay `'idle'` for rooms configured with `start_audio_off: true` and `start_video_off: true`, because no device access has been requested yet. Access is requested when you turn a track on.
</Note>

## Toggle camera and mic

Turning tracks on and off is not part of `useDevices`. It is an imperative action, so use [`useDaily`](/reference/daily-react/use-daily) and call `setLocalVideo()` / `setLocalAudio()`. Read the current on/off state reactively from the local participant's track:

```jsx theme={null}
import { useDaily, useLocalSessionId, useMediaTrack } from '@daily-co/daily-react';

function MediaToggles() {
  const daily = useDaily();
  const localSessionId = useLocalSessionId();
  const video = useMediaTrack(localSessionId, 'video');
  const audio = useMediaTrack(localSessionId, 'audio');

  return (
    <div>
      <button onClick={() => daily?.setLocalVideo(video.isOff)}>
        {video.isOff ? 'Turn camera on' : 'Turn camera off'}
      </button>
      <button onClick={() => daily?.setLocalAudio(audio.isOff)}>
        {audio.isOff ? 'Unmute' : 'Mute'}
      </button>
    </div>
  );
}
```

Passing `video.isOff` to `setLocalVideo` reads as "set video to whatever off currently is not," i.e. toggle it.

## Apply background blur and other input processors

[`useInputSettings`](/reference/daily-react/use-input-settings) wraps the input-processing pipeline. Use it to enable background blur, virtual backgrounds, or noise cancellation:

```jsx theme={null}
import { useInputSettings } from '@daily-co/daily-react';

function BlurToggle() {
  const { updateInputSettings } = useInputSettings();

  const enableBlur = () =>
    updateInputSettings({
      video: { processor: { type: 'background-blur', config: { strength: 0.5 } } },
    });

  return <button onClick={enableBlur}>Blur my background</button>;
}
```

<Warning>
  Calls to `updateInputSettings()` before the meeting is joined are silently ignored. Apply input settings after `joined-meeting`.
</Warning>

## Next steps

<CardGroup cols={2}>
  <Card title="Permissions" icon="lock" href="/docs/daily-react/docs/permissions">
    Control what each participant is allowed to send.
  </Card>

  <Card title="Rendering media" icon="video" href="/docs/daily-react/docs/rendering-media">
    Show the camera you just selected.
  </Card>
</CardGroup>
