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

# Framework guide

> Storage/picker permissions, Expo vs bare RN differences, and where to save the API key.

# Framework guide

## Expo vs bare React Native

`@markpdf/react-native` does not depend on any native modules of its own — it uses global `fetch`/`FormData`/`XMLHttpRequest`, available in both. The difference is in which picker you use to get the `uri`/`name` of the file.

### Expo: `expo-document-picker`

```bash theme={null}
npx expo install expo-document-picker
```

```tsx theme={null}
import * as DocumentPicker from "expo-document-picker";

const result = await DocumentPicker.getDocumentAsync({ type: "application/pdf" });
if (!result.canceled) {
  const asset = result.assets[0]; // { uri, name, mimeType, size }
}
```

Does not require additional permissions on `app.json` — the operating system's file chooser handles access.

### Bare RN: `react-native-document-picker`

```bash theme={null}
npm install react-native-document-picker
cd ios && pod install
```

```tsx theme={null}
import DocumentPicker from "react-native-document-picker";

const [file] = await DocumentPicker.pick({ type: [DocumentPicker.types.pdf] });
// { uri, name, type, size }
```

On Android, check that your `targetSdkVersion` is compatible with the version of `react-native-document-picker` you install — recent versions of Android Scoped Storage change how `content://` URIs are resolved. Follow the package installation guide for your version of RN.

## Permisos

Neither `expo-document-picker` nor `react-native-document-picker` require explicit storage permissions in most cases — both delegate to the system's native file picker (Document Picker on iOS, Storage Access Framework on Android), which does not need `READ_EXTERNAL_STORAGE`. If your app also needs to access the photo gallery to attach scanned images, use `expo-image-picker` (Expo) or `react-native-image-picker` (bare) and follow their separate permission requirements — that's separate from `@markpdf/react-native`.

## Where does the API key live?

Unlike a web app, a mobile app binary does not have a "server" that serves environment variables at runtime — any string embedded in the JS bundle is removable by someone with the `.ipa`/`.apk`. Options, from most to least safe:

1. **Proxy on your backend** (recommended for production): the app calls your own endpoint, which in turn calls markpdf with the key saved server-side. The app never sees the markpdf key.
2. **Embedded low privilege key**: if you do not have your own backend, use a key dedicated only to that app, with limited usage and billing limits, assuming it can be leaked.
3. **Build environment variables** (`EAS Secrets` in Expo, `.env` + `react-native-config` in bare): they prevent hardcoding the key in the versioned source code, but they **do not** prevent it from ending up in the final decompilable bundle. Useful to not commit the key to the repo, not to hide it from the end user.

```ts theme={null}
// Expo: read at build time from EAS Secrets/app.config.ts -> extra
import Constants from "expo-constants";
const client = new MarkpdfClient({ apiKey: Constants.expoConfig?.extra?.markpdfApiKey });
```

<Warning>
  If your app handles sensitive documents from real users, use option 1 (proxy in your backend). A key embedded in the binary, even if it is not in the repo, is still removable with standard decompilation tools.
</Warning>

## Networking en segundo plano

Uploading large PDFs from the picker may take time; If the user minimizes the app during upload, iOS/Android may suspend the network task depending on the operating system and the state of the app. For long, reliable background uploads, consider a background upload library (e.g. `react-native-background-upload`) rather than relying on `fetch`/`XMLHttpRequest` to survive indefinitely with the app in the background — this is a general RN limitation, not specific to `@markpdf/react-native`.
