---
title: Camera API
description: >-
  The Camera API provides access to the device's camera, enabling photo capture
  directly within POS UI extensions. The API requests camera permissions if not
  already enabled, opens the native camera interface, and returns the image data
  including dimensions, file size, and base64 string for immediate display or
  server upload.
api_version: 2026-07-rc
source_url:
  html: >-
    https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/target-apis/platform-apis/camera-api
  md: >-
    https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/target-apis/platform-apis/camera-api.md
---

# Camera API

The Camera API provides access to the device's camera, enabling photo capture directly within POS UI extensions. The API requests camera permissions if not already enabled, opens the native camera interface, and returns the image data including dimensions, file size, and base64 string for immediate display or server upload.

### Use cases

* **Photo capture:** Capture photos for product documentation, damage verification, or visual records.
* **Proof workflows:** Create photo-based proof workflows for deliveries, returns, or customer verification.
* **Visual documentation:** Document inventory conditions or store displays.
* **Customer engagement:** Implement features requiring visual capture for custom orders or personalization.

### Support Targets (28)

### Supported targets

* [pos.​cart.​line-item-details.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/cart-details#cart-details-targets)
* [pos.​cart.​line-item-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/cart-details#cart-details-action-modal-)
* [pos.​customer-details.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/customer-details#customer-details-action-menu-item-)
* [pos.​customer-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/customer-details#customer-details-action-modal-)
* [pos.​customer-details.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/customer-details#customer-details-targets)
* [pos.​draft-order-details.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/draft-order-details#draft-order-details-action-menu-item-)
* [pos.​draft-order-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/draft-order-details#draft-order-details-action-modal-)
* [pos.​draft-order-details.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/draft-order-details#draft-order-details-targets)
* [pos.​exchange.​post.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-exchange#post-exchange-action-menu-item-)
* [pos.​exchange.​post.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-exchange#post-exchange-action-modal-)
* [pos.​exchange.​post.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-exchange#post-exchange-targets)
* [pos.​home.​modal.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/home-screen#home-screen-action-modal-)
* [pos.​home.​tile.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/home-screen#home-screen-targets)
* [pos.​order-details.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/order-details#order-details-action-menu-item-)
* [pos.​order-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/order-details#order-details-action-modal-)
* [pos.​order-details.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/order-details#order-details-targets)
* [pos.​product-details.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/product-details#product-details-action-menu-item-)
* [pos.​product-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/product-details#product-details-action-modal-)
* [pos.​product-details.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/product-details#product-details-targets)
* [pos.​purchase.​post.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-purchase#post-purchase-action-menu-item-)
* [pos.​purchase.​post.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-purchase#post-purchase-action-modal-)
* [pos.​purchase.​post.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-purchase#post-purchase-targets)
* [pos.​register-details.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/register-details#register-details-targets)
* [pos.​register-details.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/register-details#register-details-action-modal-)
* [pos.​register-details.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/register-details#register-details-block-)
* [pos.​return.​post.​action.​menu-item.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-return#post-return-action-menu-item-)
* [pos.​return.​post.​action.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-return#post-return-action-modal-)
* [pos.​return.​post.​block.​render](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc/targets/post-return#post-return-targets)

### Properties

The [`shopify` global object](https://shopify.dev/docs/api/pos-ui-extensions/2026-07-rc#target-apis-define-what-your-extension-does) provides camera capabilities for the POS device. Access the following properties on `shopify` to capture photos for documentation, verification, or customer engagement.

* **takePhoto**

  **(options?: CameraMediaOptions) => Promise\<CameraMediaResponse>**

  **required**

  Launch the device's camera to take a photo.

### CameraMediaOptions

Specifies configuration options for capturing photos using the device camera.

* facingMode

  The camera that will be active when the camera interface first opens. - \`'user'\`: The front-facing camera - \`'environment'\`: The rear-facing camera

  ```ts
  'user' | 'environment'
  ```

* maxHeight

  The maximum height (1 to 1920) of the image in pixels. Resizes the image to this height if it is larger.

  ```ts
  number
  ```

* maxWidth

  The maximum width (1 to 1920) of the image in pixels. Resizes the image to this width if it is larger.

  ```ts
  number
  ```

* quality

  The quality of the image returned. Percentile value between 0 (lowest quality/highest compression) and 1 (highest quality/lowest compression).

  ```ts
  number
  ```

### CameraMediaResponse

Represents the captured image and associated metadata returned by \`shopify.camera.takePhoto()\`.

* base64

  The image data as base64 string.

  ```ts
  string
  ```

* fileSize

  The file size of the image in bytes.

  ```ts
  number
  ```

* height

  The height of the image in pixels.

  ```ts
  number
  ```

* type

  The MIME type of the image.

  ```ts
  string
  ```

* width

  The width of the image in pixels.

  ```ts
  number
  ```

Examples

### Examples

* ####

  ##### Description

  This example demonstrates capturing a photo using \`shopify.camera.takePhoto()\` and uploading it to a backend server for further processing. It shows loading states during capture and upload, handles errors appropriately, and confirms successful upload with toast notifications.

  ##### jsx

  ```tsx
  import {render} from 'preact';
  import {useState} from 'preact/hooks';

  export default async () => {
    render(<Extension />, document.body);
  };

  const Extension = () => {
    const [isProcessing, setIsProcessing] = useState(false);

    const handleCaptureAndUpload = async () => {
      setIsProcessing(true);
      try {
        const photo = await shopify.camera.takePhoto({
          quality: 0.8,
          maxWidth: 1520,
          maxHeight: 1520,
        });

        // Upload the image to your backend server
        // (Replace with your actual backend endpoint)
        await fetch('https://your-backend.com/api/upload', {
          method: 'POST',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify({
            image: photo.base64,
            mimeType: photo.type,
          }),
        });

        shopify.toast.show('Photo uploaded successfully!');
      } catch (error) {
        shopify.toast.show(`Error: ${error.message}`);
      } finally {
        setIsProcessing(false);
      }
    };

    return (
      <s-tile
        heading="Upload Photo"
        onClick={handleCaptureAndUpload}
        disabled={isProcessing}
      />
    );
  };
  ```

* ####

  ##### Description

  This example demonstrates using \`shopify.camera.takePhoto()\` to capture an image with the device camera and displaying it immediately using the \`image\` component.

  ##### jsx

  ```tsx
  import {render} from 'preact';
  import {useState} from 'preact/hooks';

  export default async () => {
    render(<Extension />, document.body);
  };

  const Extension = () => {
    const [imageData, setImageData] = useState(null);
    const [isCapturing, setIsCapturing] = useState(false);

    const handleTakePhoto = async () => {
      setIsCapturing(true);
      try {
        const photo = await shopify.camera.takePhoto();
        setImageData(photo);
        shopify.toast.show('Photo captured successfully!');
      } catch (error) {
        // skip showing errors when the user cancels the photo capture.
        if (!error.message.includes('User cancelled')) {
          shopify.toast.show(`Error: ${error.message}`);
        }
      } finally {
        setIsCapturing(false);
      }
    };

    return (
      <s-page heading="Camera Capture">
        <s-scroll-box>
          <s-stack>
            <s-button onClick={handleTakePhoto} disabled={isCapturing}>
              {isCapturing ? 'Capturing...' : 'Take Photo'}
            </s-button>

            {imageData && (
              <>
                <s-image
                  src={`data:${imageData.type};base64,${imageData.base64}`}
                />
                <s-section heading="Image Details">
                  <s-text>Width: {imageData.width}px</s-text>
                  <s-text>Height: {imageData.height}px</s-text>
                  <s-text>
                    File Size: {(imageData.fileSize / 1024).toFixed(2)} KB
                  </s-text>
                  <s-text>Type: {imageData.type}</s-text>
                </s-section>
              </>
            )}
          </s-stack>
        </s-scroll-box>
      </s-page>
    );
  };
  ```

***

## Best practices

* **Optimize image quality:** Use appropriate quality and size settings to balance image quality with file size and upload performance.
* **Provide feedback:** Show loading states while processing images and clear success/error messages after capture.
* **Handle errors gracefully:** Account for scenarios where users cancel, camera is unavailable, or permissions are denied.

***

## Limitations

* Camera functionality requires the device to have a camera and appropriate permissions granted by the user.
* Only one camera operation can be in progress at a time. Attempting to call `takePhoto()` while a capture is already in progress will result in a rejected promise.
* Base64 strings can be memory-intensive for large images. Use appropriate `maxWidth`, `maxHeight`, and `quality` settings to optimize performance.
* The `facingMode` parameter may not behave consistently on all Android devices, because camera-facing behavior varies across manufacturers. If a requested mode isn't supported, the rear-facing camera is used by default, and users can still manually switch cameras from the camera interface.

***
