---
title: useUserMedia
description: "Reactive wrapper around the [MediaDevices.getUserMedia()](https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia) API. Provides start/stop/restart controls and exposes the media stream as an observable."
category: Sensors
sidebar:
  order: 9
type-table:
  import: "@usels/web"
  name: "UseUserMedia"
---

## Demo

## Usage

<CodeTabs>
  <Fragment slot="hook">
    ```tsx twoslash
    // @noErrors
    import { useUserMedia } from "@usels/web";

    function CameraFeed() {
      const { isSupported$, stream$, enabled$, start, stop } = useUserMedia({
        constraints: { audio: false, video: true },
      });

      return (
        <div>
          <p>Supported: {isSupported$.get() ? "Yes" : "No"}</p>
          <p>Streaming: {enabled$.get() ? "Yes" : "No"}</p>
          <button onClick={() => start()}>Start</button>
          <button onClick={() => stop()}>Stop</button>
        </div>
      );
    }
    ```

  </Fragment>
  <Fragment slot="scope">
    ```tsx
    import { createUserMedia } from "@usels/web";

    function CameraFeed() {
      "use scope";
      const { isSupported$, stream$, enabled$, start, stop } = createUserMedia({
        constraints: { audio: false, video: true },
      });

      return (
        <div>
          <p>Supported: {isSupported$.get() ? "Yes" : "No"}</p>
          <p>Streaming: {enabled$.get() ? "Yes" : "No"}</p>
          <button onClick={() => start()}>Start</button>
          <button onClick={() => stop()}>Stop</button>
        </div>
      );
    }
    ```

  </Fragment>
</CodeTabs>

### Auto-start on mount

```tsx
// @noErrors
import { useUserMedia } from "@usels/web";

function AutoCamera() {
  const { stream$, stop } = useUserMedia({
    constraints: { audio: false, video: true },
    immediate: true,
  });

  // Stream starts automatically on mount
  // Stops automatically on unmount
  return <button onClick={() => stop()}>Stop</button>;
}
```

### Audio-only capture

```tsx
// @noErrors
import { useUserMedia } from "@usels/web";

function Microphone() {
  const { stream$, start, stop, enabled$ } = useUserMedia({
    constraints: { audio: true, video: false },
  });

  return (
    <button onClick={() => (enabled$.get() ? stop() : start())}>
      {enabled$.get() ? "Stop Recording" : "Start Recording"}
    </button>
  );
}
```

### Reactive options

Options can be passed as plain values, per-field `Observable`s, or a single `Observable<UseUserMediaOptions>`. `constraints` are read at each `start()` / `restart()` call.

```typescript
import { observable } from "@usels/core";
import { useUserMedia } from "@usels/web";

const constraints$ = observable<MediaStreamConstraints>({ audio: false, video: true });

const { start, restart } = useUserMedia({ constraints: constraints$ });

// Later: switch to audio+video reactively
constraints$.set({ audio: true, video: true });
restart(); // uses updated constraints
```

## Notes

**`options` is `DeepMaybeObservable`.** Each option field can be a plain value or an `Observable`. `constraints` are read at each `start()` / `restart()` call time. `immediate` is read at mount-time.
