Builds Expo native modules and views with the Expo Modules API (Swift, Kotlin, TypeScript), including create-expo-module, expo-module.config.json, config plugins, and lifecycle hooks. Use when adding native functionality, native views, or platform support to an Expo module. Not for Expo Router screens, JS-only Expo app features, or React Native Turbo Modules outside the Expo Modules API.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Builds Expo native modules and views with the Expo Modules API (Swift, Kotlin, TypeScript), including create-expo-module, expo-module.config.json, config plugins, and lifecycle hooks. Use when adding native functionality, native views, or platform support to an Expo module. Not for Expo Router screens, JS-only Expo app features, or React Native Turbo Modules outside the Expo Modules API.
Complete reference for building native modules and views using the Expo Modules API. Covers Swift (iOS), Kotlin (Android), and TypeScript.
When to Use
Creating a new Expo native module or native view
Adding native functionality (camera, sensors, system APIs) to an Expo app
Wrapping platform SDKs for React Native consumption
Building config plugins that modify native project files
Adding Android, Apple, or web support to an existing Expo module
Editing expo-module.config.json, config plugins, or lifecycle hooks
Prerequisites
Node.js and a package manager (npm, yarn, pnpm, bun)
Expo SDK installed in the host app
For iOS: macOS with Xcode and CocoaPods
For Android: Android Studio with Android SDK
Windows host (PowerShell) is the primary environment; commands below are PowerShell-compatible
Procedure
1. Choose the scaffold type
Local module — for a single app. Lives in expo.autolinking.nativeModulesDir when configured, otherwise in modules/.
Standalone module — for reuse, monorepos, or publishing. Has its own package metadata, scripts, and usually an example app.
2. Scaffold with create-expo-module
Prefer create-expo-module over manually creating native module files and directories. The scaffold sets up the expected layout, expo-module.config.json, podspec or Gradle files, TypeScript bindings, and the standalone example app flow.
--barrel — generate an index.ts barrel (local modules do not generate one by default)
--package-manager — specify npm, yarn, pnpm, or bun
--name — changes the native class name, the folder name
not
Non-interactive mode: pass the positional slug or path explicitly
Load references/create-expo-module.md before scaffolding or extending a module. It covers local vs standalone modules, all flags, expo.autolinking.nativeModulesDir, and add-platform-support behavior and quirks.
3. Add a platform to an existing module
If an existing Expo module only needs another platform, use add-platform-support instead of manually copying native directories:
4. Replace generated example code with the real implementation
Feature examples are opt-in. A newly scaffolded module may be minimal if no features were selected. Replace the generated samples with your real implementation.
Load references/native-module.md when writing module definitions. It covers the full DSL: Name, Function, AsyncFunction, Property, Constant, Events, the type system, and shared objects.
Load references/native-view.md when building native view components. It covers View, Prop, EventDispatcher, view lifecycle, and ref-based functions.
Load references/lifecycle.md when implementing lifecycle hooks: module lifecycle, iOS app/AppDelegate listeners, and Android activity/application listeners.
Load references/config-plugin.md when building config plugins that modify Info.plist, AndroidManifest.xml, or read values in native code.
iOS uses just the class name; Android uses the fully-qualified class name (package + class).
Load references/module-config.md when editing expo-module.config.json. It covers all fields, file placement, and autolinking behavior.
Pitfalls
iOS vs Android class names in config: iOS uses just the class name (MyModule); Android uses the fully-qualified class name (expo.modules.mymodule.MyModule). Mixing these up causes autolinking failures.
--name does not change the folder name: --name changes the native class name only. In non-interactive local scaffolding, pass the positional slug or path explicitly to control the folder name.
No barrel by default for local modules: Local modules do not generate an index.ts barrel by default. Use --barrel only if you want one.
ViewEvent implies View: Selecting ViewEvent as a feature will also scaffold view-related code.
Feature examples are opt-in: A newly scaffolded module may be minimal if no features were selected. Do not assume all DSL elements are present after scaffolding.
Prefer add-platform-support over manual copying: Manually copying native directories can miss podspec/Gradle wiring. Always use the CLI subcommand.
Local vs standalone tooling: Local modules use the host app's tooling; standalone modules have their own package metadata, scripts, and example app. Do not mix these workflows.
Verify against current docs: Verify commands, API behavior, pricing, quotas, credentials, and deployment effects against current official documentation before making changes. Do not treat generated examples as a substitute for environment-specific tests, security review, or user approval for destructive or costly actions.
Verification
Confirm the module is autolinked:
npx expo prebuild --clean
Build for iOS (requires macOS):
npx expo run:ios
Build for Android:
npx expo run:android
Verify the TypeScript binding resolves without errors:
npx tsc --noEmit
Confirm expo-module.config.json is valid JSON and lists the correct class names for each platform. Check that the platforms array matches the platforms you scaffolded.