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

# Native file picker

> Use native camera, gallery, and document pickers for React Native chat attachments.

By default, the hosted chat widget's attach button uses the WebView file input. On low-RAM Android devices this can be unreliable because the system camera can kill the WebView render process while capture is in progress.

Configure `filePicker` so the attach button uses native pickers instead. Picked files are delivered to the hosted widget over the SDK bridge, mirrored to cache when configured, and re-delivered until the widget acknowledges them.

## Choose a picker path

Use one of these approaches:

* Install `@sagepilot-ai/react-native-camera-addon` for Sagepilot's Android in-app CameraX picker.
* Use `createSagepilotFilePicker(...)` from the base SDK when your app already provides camera, gallery, or document picker modules.

The base chat SDK does not bundle CameraX or CameraX Gradle dependencies.

## Android CameraX addon

Install the addon when your Android app wants Sagepilot's in-app CameraX picker:

```bash theme={null}
npm install @sagepilot-ai/react-native-camera-addon@latest
```

The addon adds these Android dependencies through its own Gradle file:

| Dependency                             | Purpose                                       |
| -------------------------------------- | --------------------------------------------- |
| `androidx.camera:camera-core`          | CameraX core APIs                             |
| `androidx.camera:camera-camera2`       | Camera2 implementation                        |
| `androidx.camera:camera-lifecycle`     | Lifecycle-aware camera binding                |
| `androidx.camera:camera-view`          | Camera preview UI                             |
| `androidx.exifinterface:exifinterface` | Image metadata handling                       |
| `androidx.activity:activity`           | Activity result and AndroidX activity support |

By default, the addon uses CameraX `1.5.3`, ExifInterface `1.4.2`, and AndroidX Activity `1.10.1`. Override them from the root Gradle project with `sagepilotCameraXVersion`, `sagepilotExifInterfaceVersion`, and `sagepilotActivityVersion` when your app needs pinned AndroidX versions.

Run a fresh native Android build after installing the addon. A Metro reload is not enough for React Native autolinking to register `NativeModules.SagepilotInAppCamera`.

For Expo apps, use a native/dev-client build:

```bash theme={null}
npx expo run:android
```

For bare React Native apps, clean and rebuild Android:

```bash theme={null}
cd android && ./gradlew clean
cd ..
npx react-native run-android
```

<Warning>
  Request and confirm Android `CAMERA` permission before users open the Sagepilot CameraX picker. Do not rely on the first camera tap inside the widget to show the runtime permission prompt. On some Android devices, that first permission flow can pause the app and cause the hosted WebView to reload.
</Warning>

Then pass the addon picker to `configure()`:

```ts theme={null}
import { SagepilotChat } from "@sagepilot-ai/react-native-sdk";
import { createSagepilotCameraXFilePicker } from "@sagepilot-ai/react-native-camera-addon";

await SagepilotChat.configure({
  key: "workspace_id:channel_id",
  filePicker: createSagepilotCameraXFilePicker({
    includeCamera: true,
    includeLibrary: true,
    includeDocuments: true
  })
});
```

`createSagepilotCameraXFilePicker()` returns `undefined` outside Android, so shared app setup can call it safely. On iOS, no additional setup is required for the CameraX addon unless your app provides its own native iOS picker.

With this adapter enabled on Android, the hosted widget routes each attachment action to its matching native source:

| Attachment action | Native source              |
| ----------------- | -------------------------- |
| Camera            | In-app CameraX capture     |
| Gallery           | Android media/photo picker |
| Files             | Android document picker    |

When the addon advertises these sources, gallery and file actions do not use a generic WebView file chooser.

## Host-provided picker modules

Use this path when your app already owns picker dependencies or needs a custom iOS picker.

Install only the modules your app needs:

```bash theme={null}
# Camera and photo gallery
npm install react-native-image-picker

# Document/file picking
npm install @react-native-documents/picker

# Reliable document reads and durable file storage
npm install react-native-blob-util

# Durable picked-file batch manifests
npm install @react-native-async-storage/async-storage
```

Then pass those modules to `createSagepilotFilePicker(...)`:

```ts theme={null}
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as imagePicker from "react-native-image-picker";
import * as documentsPicker from "@react-native-documents/picker";
import ReactNativeBlobUtil from "react-native-blob-util";
import {
  SagepilotChat,
  createAsyncStorageCacheStorage,
  createSagepilotFilePicker,
  createSagepilotFileStore
} from "@sagepilot-ai/react-native-sdk";

await SagepilotChat.configure({
  key: "workspace_id:channel_id",
  cacheStorage: createAsyncStorageCacheStorage(AsyncStorage),
  fileStore: createSagepilotFileStore(ReactNativeBlobUtil),
  filePicker: createSagepilotFilePicker({
    imagePicker,
    documentsPicker,
    fileReader: ReactNativeBlobUtil
  })
});
```

The base SDK uses only the picker modules you pass into `createSagepilotFilePicker(...)`.

## Durable recovery

For the strongest recovery path, pass both `cacheStorage` and `fileStore`.

`cacheStorage` stores the picked-file batch manifest. `fileStore` stores picked-file bytes in app-private storage so a captured photo or document can survive an app-process restart within the hosted widget's upload limits.

## Upload limits

The hosted widget enforces the final attachment limits for both WebView and native picker flows.

| Limit              | Value             |
| ------------------ | ----------------- |
| Files per send     | Up to 5 files     |
| Previewable images | Up to 5 MB total  |
| Non-image files    | Up to 10 MB total |

The native picker also applies earlier safety guards before files are read into memory.

| Guard                | Default            |
| -------------------- | ------------------ |
| Gallery multi-select | 5 files            |
| Documents            | Reject above 20 MB |
| Images               | Reject above 15 MB |

These picker guards reduce out-of-memory risk. They do not raise the hosted widget's final upload limits.

## Platform notes

* The CameraX addon is Android-only. It owns CameraX dependencies, the native module, camera activity, overlay, and image processing.
* Camera and gallery images are downscaled natively to keep memory and upload sizes low.
* Picker failures are surfaced with typed error codes such as `permission_denied`, `camera_unavailable`, `encode_failed`, `file_too_large`, and `read_failed`.
* Android apps that use the CameraX addon must grant `CAMERA` at runtime before opening the Sagepilot picker. If permission is missing or denied, ask users to grant it from your app flow or Android settings before presenting the camera action again.
* No additional iOS setup is required for the CameraX addon. Add iOS permission strings only if your app separately provides its own native iOS picker.
* Apps that skip `filePicker` keep the WebView file input. The SDK can recover from WebView renderer crashes, but a photo captured at crash time cannot be restored in that mode.
