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 undernode_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.
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 |
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 |
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 |
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 |
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 |
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-screenreact-native-safe-area-context,react-native-screens,react-native-gesture-handler,react-native-reanimated,react-native-url-polyfillnativewind+tailwindcss@supabase/supabase-js@tanstack/react-query,react-hook-form,zodlucide-react-native
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 |
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-nativewhich Expo supports, and tells you the rebuild is required.
Detection in the editor
When you save apackage.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
Builds & Submissions
Run the Build & Ship for changes that need it.
Phone-Frame Preview
Test JS-only changes in preview before any rebuild.
Mobile Quickstart
Full path from project creation to first TestFlight upload.
Mobile Credentials
Make sure your Apple + Google credentials are connected before triggering a native build.