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

# Native Modules

> Which packages work in the phone-frame preview, which need a Build & Ship, and which to avoid

A "native module" is any React Native package that ships compiled iOS/Android code — anything that pokes at the device's hardware or OS APIs. These behave differently in the phone-frame preview (which runs React Native Web) than on a real device. This page is the cheat sheet so you don't waste a 30-minute build finding out.

## The three buckets

| Bucket                     | Behavior                                             | Examples                                                                           |
| -------------------------- | ---------------------------------------------------- | ---------------------------------------------------------------------------------- |
| **Pure JS**                | Works everywhere identically                         | Most utility libs, date-fns, zod, react-hook-form, lodash                          |
| **Web-supported native**   | Works in preview AND on device                       | NativeWind, React Navigation, expo-router, expo-linking, expo-image (web fallback) |
| **Native-only**            | Stub or no-op in preview, real on device             | expo-camera, expo-notifications, expo-secure-store, expo-haptics                   |
| **Native + needs rebuild** | Native-only AND requires Build & Ship to take effect | All of the above when added/upgraded                                               |

## When you need a Build & Ship vs OTA

The decisive question: **does the package add files under `node_modules/.../ios/` or `android/`?**

* **Yes** → adding or upgrading the package means a Build & Ship. The previous binary doesn't have the new native code; an OTA can't deliver it.
* **No** → it's pure JS. OTA can deliver the change.

You can check this fast: after `npm install <package>`, look in `node_modules/<package>/`. If you see `ios/` or `android/` directories, it's native.

The Hiveku AI knows this — when you ask it to add `expo-camera`, it'll include "you'll need to Build & Ship after this lands" in its response. If you ask it to add `date-fns`, it'll say "OTA is fine."

## Common native modules and their preview behavior

### Hardware access

| Module                                    | Preview behavior                              | Real device                 |
| ----------------------------------------- | --------------------------------------------- | --------------------------- |
| `expo-camera`                             | Black rectangle (no fallback)                 | Real camera                 |
| `expo-image-picker`                       | Browser file picker                           | Native picker (camera roll) |
| `expo-barcode-scanner`                    | Black rectangle                               | Real scanner                |
| `expo-location`                           | Returns `null` from `getCurrentPositionAsync` | Real GPS                    |
| `expo-sensors` (Accelerometer, Gyroscope) | All zeros                                     | Real sensor data            |
| `expo-haptics`                            | No-op                                         | Real haptic feedback        |

If you build a "scan my receipt" feature, the preview can't validate it. Publish to Preview channel and open on a real phone with Expo Go to test.

### Storage & secrets

| Module                                      | Preview behavior                      | Real device                     |
| ------------------------------------------- | ------------------------------------- | ------------------------------- |
| `expo-secure-store`                         | Falls back to localStorage (insecure) | iOS Keychain / Android Keystore |
| `@react-native-async-storage/async-storage` | localStorage                          | Native AsyncStorage             |
| `expo-file-system`                          | No-op (read-only mock)                | Real filesystem access          |
| `expo-sqlite`                               | Stub                                  | Real SQLite                     |

The Supabase client in the starter uses `expo-secure-store` for session persistence on device, and falls back to localStorage on web — so authentication works in both. Custom encryption flows that *require* hardware-backed crypto (Keychain) won't actually be hardware-backed in preview.

### Notifications & background

| Module                  | Preview behavior | Real device             |
| ----------------------- | ---------------- | ----------------------- |
| `expo-notifications`    | No-op            | Real push notifications |
| `expo-task-manager`     | No-op            | Real background tasks   |
| `expo-background-fetch` | No-op            | Real background fetch   |
| `expo-keep-awake`       | No-op            | Keeps screen on         |

These only work after a Build & Ship to TestFlight + a real device install. There's no preview path; OTA can't deliver them either if not already in the binary.

### Auth providers

| Module                                      | Preview behavior               | Real device                     |
| ------------------------------------------- | ------------------------------ | ------------------------------- |
| `expo-auth-session` (Google, Apple)         | Falls back to web flow (popup) | Native sheet                    |
| `expo-apple-authentication`                 | Refuses (not supported on web) | Real "Sign in with Apple" sheet |
| `@react-native-google-signin/google-signin` | Web SDK fallback               | Native flow                     |

Sign-in with Apple is required by App Store rules if you also offer Google sign-in. Add the entitlement in `app.json` and Build & Ship — no preview path.

### UI / animation

| Module                         | Preview behavior                  | Real device                        |
| ------------------------------ | --------------------------------- | ---------------------------------- |
| `react-native-reanimated`      | Works                             | Works (faster — runs on UI thread) |
| `react-native-gesture-handler` | Mostly works (web pointer events) | Native gestures                    |
| `react-native-skia`            | Works (slow on web)               | Works (native GPU)                 |
| `lottie-react-native`          | Works                             | Works                              |
| `expo-blur`                    | Works (web blur filter)           | Native blur                        |

Heavy animation testing is best on a real device — the web build's frame budget is different from native. Layout and basic transitions are fine to validate in preview.

### Common picks for the v1 starter

The Native Mobile starter ships with:

* `react-native`, `react`, `expo`, `expo-router`, `expo-status-bar`, `expo-secure-store`, `expo-splash-screen`
* `react-native-safe-area-context`, `react-native-screens`, `react-native-gesture-handler`, `react-native-reanimated`, `react-native-url-polyfill`
* `nativewind` + `tailwindcss`
* `@supabase/supabase-js`
* `@tanstack/react-query`, `react-hook-form`, `zod`
* `lucide-react-native`

All of these work in both preview and on device. **Adding any of them to your project does not require a special build — they're already linked.**

## Packages to think twice about

Some packages are technically supported by Expo but bring a steep cost. Avoid unless you really need them:

| Package                            | Cost                                                | Alternative                                       |
| ---------------------------------- | --------------------------------------------------- | ------------------------------------------------- |
| `react-native-vision-camera`       | Heavy bundle (+8 MB), requires custom config        | `expo-camera` for most use cases                  |
| `react-native-skia`                | Heavy bundle (+14 MB), GPU-bound                    | CSS gradients via NativeWind for simple effects   |
| `react-native-firebase`            | Conflicts with Expo's native modules, complex setup | `firebase` JS SDK (works fine for most use cases) |
| `react-native-webview`             | Adds real WebView native code                       | `Linking.openURL` for one-off external links      |
| `react-native-bottom-sheet`        | Forces a config plugin + custom build               | Modal stack from Expo Router                      |
| Custom audio/video processing libs | Native compilation breaks frequently                | Expo AV is the supported path                     |

The AI in the chat will tell you if you ask for one of these, suggesting the alternative. You can override and continue, but expect a longer first build.

## What an AI agent will and won't do for you

The AI knows the difference between OTA and Build & Ship. When you ask it to add a feature:

* **JS-only feature** ("add a search bar to the items list") — applies via OTA, no rebuild. AI says: "Published to preview. Refresh your phone."
* **Native-dep feature** ("add camera-based receipt scanning") — AI installs `expo-camera`, modifies the screen, and includes "this needs a Build & Ship — click the button on the Builds page" in its response.
* **Forbidden combo** ("add Stripe Native SDK") — AI refuses or steers you to `@stripe/stripe-react-native` which Expo supports, and tells you the rebuild is required.

When the AI is wrong (it sometimes thinks a package is pure-JS when it ships native code), you'll find out at build time. The decoded error usually points at the package — re-trigger with **Fix with AI** and it'll switch course.

## Detection in the editor

When you save a `package.json` with a new native dependency, the editor flashes a banner:

> **Native dependency added: `expo-camera`** — this change needs a Build & Ship to reach devices. OTA Update will not deliver it.

That's your prompt to plan the build. You can keep iterating in JS first (say, building the UI for the camera feature with placeholders), then click Build & Ship once the surrounding code is solid.

## What's next

<CardGroup cols={2}>
  <Card title="Builds & Submissions" icon="rocket" href="/mobile/builds">
    Run the Build & Ship for changes that need it.
  </Card>

  <Card title="Phone-Frame Preview" icon="mobile" href="/mobile/preview">
    Test JS-only changes in preview before any rebuild.
  </Card>

  <Card title="Mobile Quickstart" icon="rocket" href="/mobile/quickstart">
    Full path from project creation to first TestFlight upload.
  </Card>

  <Card title="Mobile Credentials" icon="key" href="/mobile/credentials">
    Make sure your Apple + Google credentials are connected before triggering a native build.
  </Card>
</CardGroup>
