---
title: "Camera"
description: "Preview and capture photos."
---

Use `@ink/camera` to capture photos with the front or rear camera.

## Permissions

| Permission | Required for |
| --- | --- |
| `camera` | Camera preview and photo capture. |

```ts
import { lightos } from "@ink/lightos";

const permission = await lightos.requestPermission("camera");
```

Use `lightos.getPermission("camera")` to check without a prompt. See [Request a permission](/permissions-guide) for the returned statuses.

## Capture a photo

<div className="pattern-example">
<div>

```tsx
import { Screen } from "ink";
import { CameraPreview, useCamera } from "@ink/camera";

export function Capture() {
  const camera = useCamera({ facing: "back" });
  return (
    <Screen title="Photo">
      <CameraPreview controller={camera} />
    </Screen>
  );
}
```

</div>
<div className="pattern-preview">
**Camera**

<img src="/images/patterns/camera-preview.png" alt="The camera preview in the emulator." width="1080" height="1240" />

**Review**

<img src="/images/patterns/camera-review.png" alt="A captured photo with Retake and Use photo actions." width="1080" height="1240" />

</div>
</div>

`useCamera` opens the camera after React commits the visible screen. It does not request permission; handle that before opening the preview.

Tapping the preview captures a photo. The capture flash follows the theme: black in dark mode or white in light mode.

### Review the photo

`CameraPreview` shows the full frame while Ink saves the full-resolution JPEG. During saving, **Use photo** is disabled and labelled **Saving photo**. **Retake** remains available.

The review image stays at screen resolution after saving finishes.

## Save the result

After acceptance, `camera.state.value` contains `capturedAt` and a [FileRef](/files#file-metadata) in `file`. Image dimensions and MIME type are on `file`.

Save `photo.file.id` with your app’s data to reopen an accepted photo later. Photos stay in private app storage across app restarts.

Remove accepted photos with `files.remove(photo.file.id)` from `@ink/files`. Unaccepted photos are removed when the session closes.

Use `photo.file.src` to read or display the image. Earlier saved camera URLs still resolve, but new captures expose only the managed file.

## Custom capture controls

### Capture

Use the `camera` controller from the example:

```ts
await camera.capture();
```

This requires a mounted preview and a ready controller. The promise resolves when the camera accepts the command, before the user accepts the photo.

### Accept

```ts
if (camera.state.reviewSource && !camera.state.saving) {
  await camera.accept();
}
```

Disable acceptance until `reviewSource` exists and `saving` is false. `reviewSource` is temporary; read `state.value` for the accepted photo.

### Retake

```ts
await camera.retake();
```

## Choose a camera

To use the front camera, change the hook in your component:

```ts
const camera = useCamera({ facing: "front" });
```

Missing hardware reports an unavailable error; Ink does not substitute another camera.

### Leaving the screen

The component releases the camera when covered or backgrounded and reopens it when visible again.

Interrupted capture either rejects or completes for the original request; it cannot update a replacement screen.

Handle permission denial, camera-in-use errors and unavailable hardware separately.

Use [Barcode](/barcode) to scan codes or [MediaPicker](/media-picker) to choose existing photos. `@ink/camera` does not include the barcode scanner.
