Native Modules Best Practices
A condensed summary of the 25 most important native module practices drawn from every page in this section.
Search across all documentation pages
A condensed summary of the 25 most important native module practices drawn from every page in this section.
Search Expo SDK packages first: expo-camera, expo-location, expo-secure-store, and 100+ modules ship with SDK 57 native binaries - prefer them over random npm native wrappers per Native Module Rules.
Run the third-party checklist before npm install: Verify RN 0.86, New Architecture support, config plugin availability, and maintenance - see Using Third-Party Native Libraries.
Always npx expo install for native deps: Resolves versions compatible with SDK 57 native binaries - compile success does not guarantee runtime stability.
Treat Expo Go as SDK-only: Third-party and custom native modules require expo-dev-client and a development build - never sign off QA from Expo Go alone.
Rebuild native after every native package.json change: Metro hot reload does not compile Swift/Kotlin - run npx expo run:ios / eas build before claiming done.
OTA cannot ship native code: EAS Update delivers JavaScript and assets only - native changes need a new store binary or dev client.
Express permissions in config plugins: iOS NS*UsageDescription and Android manifest entries belong in app.config plugins - see Config Plugins.
Write idempotent plugins: Running npx expo prebuild twice must not duplicate Info.plist or AndroidManifest entries - git diff after second run should be empty.
Document plugin order in app.config.ts: Later plugins win when modifying the same key - comment why expo-router precedes custom plugins.
Prefer expo-build-properties for Gradle/Pod pins: compileSdkVersion, deployment target, and ProGuard rules survive regeneration - see Patching Native Projects.
Never hand-edit ios/ and android/ in CNG apps: Next prebuild --clean wipes edits - encode changes in plugins per Prebuild Basics.
Use create-expo-module for app-local bridges: Expo Modules API integrates autolinking, TypeScript, and config plugins - see create-expo-module.
Require an ADR for custom native modules: Document why SDK packages failed, name a maintainer, and plan New Architecture testing - Native Module Rules Tier 3.
Verify autolinking before Xcode/Gradle rabbit holes: npx expo-modules-autolinking resolve --platform ios shows what actually compiles into the binary.
Align Metro and native resolution in monorepos: Enable autolinkingModuleResolution and avoid duplicate react-native copies - Autolinking & expo-modules-core.
Declare workspace native packages as direct app dependencies: Transitive autolinking of packages/* modules is unreliable - app package.json must list them explicitly.
Mock at the service boundary in Jest: jest-expo stubs Expo modules; override only what tests assert - see Mocking Native Modules.
A passing Jest suite does not prove native works: Complement unit tests with device QA or Maestro on a development build.
Choose TurboModules only with justification: Codegen + C++ ceremony suits RN ecosystem libraries - app teams default to Expo Modules unless sharing native C++ core - Writing a TurboModule.
Test New Architecture on release builds: RN 0.86 defaults to Fabric/TurboModules - debug Metro can mask bridge fallback bugs; run --configuration Release on hardware.
One native dependency per PR when possible: Mixing payment SDK, maps, and custom module in one merge makes bisect impossible when prebuild fails.
Run npx expo-doctor after every native change: Catches incompatible versions, missing plugins, and misconfigured workspaces before CI.
Use patch-package only with a removal ticket: Prefer config plugins and upstream fixes - patches on node_modules break on expo install --fix.
Post-SDK-bump native survival script: expo install --fix → expo-doctor → prebuild --clean → idempotency check → release compile on both platforms.
Pre-release native checklist: Development build on device, primary native user flows, permission denial paths, expo-modules-autolinking resolve clean, plugin idempotency, and store credential rebuild if native deps changed.
Skim this summary first for orientation, then deep-dive individual pages when implementing. Return here before code review or SDK upgrades.
Expo Go while prototyping with SDK-only modules. Switch to expo-dev-client before merging any third-party or custom native dependency.
Register the library's config plugin in app.config.ts, run npx expo prebuild --clean, and verify usage description strings exist in generated Info.plist.
CNG regenerates native projects from config. Native modules install via autolinking; config plugins apply their permissions and build requirements during prebuild - CNG prebuild.
When Expo SDK, maintained third-party packages, and thin Expo modules cannot meet a documented requirement - with ADR, owner, and dev-client QA plan per Native Module Rules.
Stack versions: This page was written for React 19.2.3, React Native 0.86.0, and Expo SDK 57 (
expo~57.0.4).
Reviewed by Chris St. John·Last updated Jul 16, 2026