OnActivityResult lifecycle listener (Android)
The OnActivityResult component defines the activity lifecycle listener that is called when the activity launched with startActivityForResult returns a result. Arguments: activity — The Android activity that received the result; payload — An object containing data about the activity result with fields: requestCode (Int) — The request code originally supplied to startActivityForResult, used to identify the source of the result; resultCode (Int) — The result code returned by the child activity (for example, Activity.RESULT_OK or Activity.RESULT_CANCELED); data — An optional intent that carries the result data returned from the launched activity. Can be null.
Module API overview and purpose
The native modules API is an abstraction layer on top of JSI and other low-level primitives that React Native is built upon. It is built with modern languages (Swift and Kotlin) and provides an easy-to-use and convenient API that is consistent across platforms where possible.
Module definition components overview
Each module class must implement a definition function. The module definition consists of DSL components that describe the module's functionality and behavior.
Name component for modules
The Name component sets the name of the module that JavaScript code will use to refer to the module. It takes a string as an argument. This can be inferred from the module's class name, but it is recommended to set it explicitly for clarity. Example: Name("MyModuleName")
Constant component for single constants
The Constant component defines a constant property on the JavaScript object. The property is computed only once when it is first accessed, and subsequent accesses return the cached value. Example in Swift: Constant("PI") { Double.pi }. Example in Kotlin: Constant("PI") { Math.PI }.
Constants component (deprecated)
The Constants component is deprecated and should be replaced with the Constant component. It set constant properties on the module and could take a dictionary or a closure that returns a dictionary.
Function component for synchronous functions
The Function component defines a native synchronous function that will be exported to JavaScript. Synchronous means that when the function is executed in JavaScript, its native code is run on the same thread and blocks further execution of the script until the native function returns. Arguments: name (String) — Name of the function that you will call from JavaScript; body ((args...) -> ReturnType) — The closure to run when the function is called. The function can receive up to 8 arguments due to limitations of generics in both Swift and Kotlin.
AsyncFunction component for asynchronous functions
The AsyncFunction component defines a JavaScript function that always returns a Promise and whose native code is by default dispatched on a different thread than the JavaScript runtime runs on. Arguments: name (String) — Name of the function that you will call from JavaScript; body ((args...) -> ReturnType) — The closure to run when the function is called. If the type of the last argument is Promise, the function will wait for the promise to be resolved or rejected before the response is passed back to JavaScript. Otherwise, the function is immediately resolved with the returned value or rejected if it throws an exception. The function can receive up to 8 arguments (including the promise).
AsyncFunction recommended use cases
It is recommended to use AsyncFunction over Function when it: does I/O bound tasks such as sending network requests or interacting with the file system; needs to be run on a different thread, for example, the main UI thread for UI-related tasks; is an extensive or long-lasting operation that would block the JavaScript thread which in turn would reduce the responsiveness of the application.
AsyncFunction runOnQueue modifier
It is possible to change the native queue of AsyncFunction by calling the .runOnQueue function on the result of that component. Example in Swift: AsyncFunction("myAsyncFunction") { (message: String) in return message }.runOnQueue(.main). Example in Kotlin: AsyncFunction("myAsyncFunction") { message: String -> return@AsyncFunction message }.runOnQueue(Queues.MAIN).
AsyncFunction with Kotlin coroutines (Android)
AsyncFunction can receive a suspendable body on Android. It must be passed in infix notation after the Coroutine block. AsyncFunction with a suspendable body cannot receive Promise as an argument. It uses a suspension mechanism to execute asynchronous calls. The function is immediately resolved with the returned value of the provided suspendable block or rejected if it throws an exception. The function can receive up to 8 arguments. By default, suspend functions are dispatched on the module's coroutine scope. Every other suspendable function called from the body block is run within the same scope. This scope's lifecycle is bound to the module's lifecycle - all unfinished suspend functions will be canceled when the module is deallocated.
Property component for JavaScript object properties
The Property component defines a new property directly on the JavaScript object that represents a native module. It is the same as calling Object.defineProperty on the module object. For read-only properties, use a shorthanded syntax with two arguments: name (String) and getter (() -> PropertyType). For mutable properties, both getter and setter closures are needed.
View component for native view support
The View component enables the module to be used as a native view. Arguments: viewType — The class of the native view that will be rendered. On Android, the provided class must inherit from ExpoView. On iOS it is optional. definition (() -> ViewDefinition) — A builder of the view definition. Definition components accepted as part of the view definition: Prop, Events, GroupView, and AsyncFunction. AsyncFunction in the view definition is added to the React ref of the React component representing the native view. Such async functions automatically receive an instance of the native view as the first argument and run on the UI thread by default.
View component SwiftUI support limitation
Support for rendering SwiftUI views is planned but not currently available. For now, you can use UIHostingController and add its content view to your UIKit view.
Events component for event names
The Events component defines event names that the module can send to JavaScript. This component can be used inside of the View block to define callback names. Example: Events("onCameraReady", "onPictureSaved", "onBarCodeScanned").
OnStartObserving component for first listener setup
The OnStartObserving component defines the function that is invoked when the first event listener is added. You need to pass an event name to scope the listener to a specific event. This is useful when you need to set up or tear down resources per-event rather than globally.
OnStopObserving component for listener removal
The OnStopObserving component defines the function that is invoked when all event listeners for a given event are removed. Like OnStartObserving, you need to pass an event name to scope the listener to a specific event.
OnCreate module lifecycle listener
The OnCreate component defines a module's lifecycle listener that is called right after module initialization. If you need to set up something when the module gets initialized, use this instead of the module's class initializer.
OnDestroy module lifecycle listener
The OnDestroy component defines a module's lifecycle listener that is called when the module is about to be deallocated. Use it instead of the module's class destructor.
OnAppContextDestroys module lifecycle listener
The OnAppContextDestroys component defines a module's lifecycle listener that is called when the app context owning the module is about to be deallocated.
OnAppBecomesActive lifecycle listener (iOS)
The OnAppBecomesActive component defines the listener that is called when the app becomes active again (after OnAppEntersForeground). This function is only available on iOS. On Android, you may want to use OnActivityEntersForeground instead.
OnActivityDestroys lifecycle listener (Android)
The OnActivityDestroys component defines the activity lifecycle listener that is called when the activity owning the JavaScript context is about to be destroyed. This function is only available on Android. On iOS, you may want to use OnAppEntersBackground instead.
OnNewIntent lifecycle listener (Android)
The OnNewIntent component defines the activity lifecycle listener that is called when the activity receives a new intent (for example, from a deep link). Arguments: intent (Intent) — The new intent was delivered to the activity. For more information about the Intent type, visit: https://developer.android.com/reference/android/content/Intent.
OnUserLeavesActivity lifecycle listener (Android)
The OnUserLeavesActivity component defines the activity lifecycle listener called during the activity lifecycle when an activity is about to go into the background because of user choice. For example, when the user presses the Home key, OnUserLeavesActivity will be called, but when an incoming phone call causes the in-call Activity to be automatically brought to the foreground, OnUserLeavesActivity will not be called on the activity being interrupted.
RegisterActivityContracts for activity result contracts (Android)
The RegisterActivityContracts component registers Android activity result contracts that let you launch activities and handle their results in a type-safe way. This is the modern replacement for startActivityForResult. Inside the RegisterActivityContracts block, use registerForActivityResult to register each contract. The registered launchers can then be used in async functions to launch activities.
Prop component for view props
The Prop component defines a setter for the view prop of given name. Arguments: name (String) — Name of view prop that you want to define a setter; defaultValue (ValueType) — Optional default value used when the setter is called with null; setter ((view: ViewType, value: ValueType) -> ()) — Closure that is invoked when the view rerenders. This property can only be used within a View closure. Props of function type (callbacks) are not supported yet.
PropGroup component for batch prop registration (Android)
The PropGroup component batch-registers multiple props that share a common setter pattern on Android. Instead of defining each prop individually, you can register them all at once with a single handler. Two overloads are available: Pair-based (each prop is a Pair<String, CustomValueType>, the handler receives the view, the mapped custom value, and the prop value) and String-based (each prop is a name string, the handler receives the view, the positional index, and the prop value). Note: PropGroup is used internally by the CSS prop decorators. Most modules should use individual Prop definitions unless they have many props with a shared setter pattern.
OnViewDidUpdateProps view lifecycle method
The OnViewDidUpdateProps component defines the view lifecycle method that is called when the view finished updating all props.
OnViewDestroys view lifecycle listener (Android)
The OnViewDestroys component creates a view's lifecycle listener that is called right after the view is no longer used by React Native. This function is only available on Android. On iOS, you may want to use the destructor of the native view to achieve similar results.
AsyncFunction in view definition for ref-based functions
Similarly to AsyncFunction inside the module definition, you can define functions attached to the view ref to allow direct modification of the native view. View async functions will always be dispatched on the main queue and can receive the view instance as the first argument.
GroupView component for view groups (Android)
The GroupView component enables the view to be used as a view group on Android. Arguments: viewType — The class of the native view. The provided class must inherit from Android ViewGroup; definition (() -> ViewGroupDefinition) — A builder of the view group definition. Definition components accepted as part of the group view definition: AddChildView, GetChildCount, GetChildViewAt, RemoveChildView, RemoveChildViewAt. This property can only be used within a View closure.
AddChildView component for group views (Android)
The AddChildView component defines action that adds a child view to the view group. Arguments: action ((parent: ParentType, child: ChildType, index: Int) -> ()) — An action that adds a child view to the view group. This property can only be used within a GroupView closure.
GetChildCount component for group views (Android)
The GetChildCount component defines action that retrieves the number of child views in the view group. Arguments: action ((parent: ParentType) -> Int) — A function that returns number of child views. This property can only be used within a GroupView closure.
GetChildViewAt component for group views (Android)
The GetChildViewAt component defines action that retrieves a child view at a specific index from the view group. Arguments: action ((parent: ParentType, index: Int) -> ChildType) — A function that retrieves a child view at a specific index from the view group. This property can only be used within a GroupView closure.
RemoveChildViewAt component for group views (Android)
The RemoveChildViewAt component defines action that removes a child view at a specific index from the view group. Arguments: action ((parent: ParentType, child: ChildType) -> ()) — A function that removes a child view at a specific index from the view group. This property can only be used within a GroupView closure.
Primitive types supported in module API
Fundamentally, only primitive and serializable data can be passed back and forth between the runtimes. Supported primitive types in Swift: Bool, Int, Int8, Int16, Int32, Int64, UInt, UInt8, UInt16, UInt32, UInt64, Float32, Double, String. Supported primitive types in Kotlin: Boolean, Int, Long, Float, Double, String, Pair. All functions and view prop setters accept all common primitive types in their respective languages as arguments, including arrays, dictionaries/maps and optionals of these primitive types.
Convertibles for custom type conversions
Convertibles are native types that can be initialized from certain specific kinds of data received from JavaScript. Such types are allowed to be used as an argument type in Function's body. For example, when the CGPoint type is used as a function argument type, its instance can be created from an array of two numbers (x, y) or a JavaScript object with numeric x and y properties.
Convertible protocol (iOS)
Convertible is a Swift protocol with one static method: convert(from value: Any?, appContext: AppContext) throws -> Self. A static method that converts a dynamically typed value from JavaScript to an instance of the Swift type conforming to Convertible. Implementers should throw an exception when the given value is invalid or of an unsupported type.
ModuleConverters for custom type conversions (Android)
On Android, modules can define custom type converters that allow non-standard types to be used as function arguments. Override the converters() method in your Module class and use the ModuleConverters builder to register converters with .from<SourceType> { } chains. Each .from<T> { } call registers a converter from type T to your custom type. At runtime, the framework tries each registered converter until one matches the incoming JavaScript value.
Built-in iOS Convertibles for CoreGraphics and UIKit types
Some common iOS types from the CoreGraphics and UIKit system frameworks are already made convertible: URL (string with a URL, file URL assumed if scheme not provided); CGFloat (number); CGPoint ({ x: number, y: number } or number[] with x and y coords); CGSize ({ width: number, height: number } or number[] with width and height); CGVector ({ dx: number, dy: number } or number[] with dx and dy vector differentials); CGRect ({ x: number, y: number, width: number, height: number } or number[] with x, y, width and height values); CGColor/UIColor (Color hex strings #RRGGBB, #RRGGBBAA, #RGB, #RGBA, named colors following CSS3/SVG specification, or "transparent"); Data (Uint8Array, SDK 50+).
Sending events from modules
To send events from native code to JavaScript/TypeScript, first provide the event names that the module can send using the Events definition component in the module definition. After that, you can use the sendEvent(eventName, payload) function on the module instance to send the actual event with some payload. To subscribe to these events in JavaScript/TypeScript, use addListener on the module object returned by requireNativeModule. Modules are extending the built-in EventEmitter class. Alternatively, you can use useEvent or useEventListener hooks.
Built-in Android Convertibles for common types
Some common Android types from packages like java.io, java.net, or android.graphics are made convertible. On Android, primitive arrays should be used whenever possible. Convertible types: java.net.URL (string with URL, scheme required); android.net.Uri / java.net.URI (string with URI, scheme required); java.io.File / java.nio.file.Path (string with path to file, Path only available on Android API 26+); android.graphics.Color (Color hex strings #RRGGBB, #RRGGBBAA, #RGB, #RGBA, named colors following CSS3/SVG specification, or "transparent"); kotlin.Pair<A, B> (Array with two values); kotlin.ByteArray (Uint8Array, SDK 50+); kotlin.BooleanArray (boolean[]); kotlin.IntArray / kotlin.FloatArray / kotlin.LongArray / kotlin.DoubleArray (number[]); kotlin.time.Duration (number represents duration in seconds, SDK 52+).
Record type for typed data objects
Record is a convertible type and an equivalent of the dictionary (Swift) or map (Kotlin), but represented as a struct where each field can have its type and provide a default value. It is a better way to represent a JavaScript object with native type safety. Record can be used as an argument of functions or the view prop setters.
Formatter API for record serialization (experimental)
The Formatter API is experimental and allows you to customize how a Record is serialized when returned from a native function. This is useful when you need to transform property values before sending them to JavaScript, or conditionally exclude certain properties from the output. Operations: map (Transform a property's value before serialization); skip (Exclude a property from the output entirely).
Formatter API skip operation usage
Use skip() to exclude a property from the output entirely. Optionally, you can provide a condition closure to conditionally skip properties based on their values or the record's state. Example in Swift: formatter.property("password", keyPath: \.password).skip(). Example in Kotlin: property(UserInfo::password).skip().
Formatter API map operation usage
Use map to transform property values before they are sent to JavaScript. Example in Swift: formatter.property("price", keyPath: \.price).map { value in "$\(String(format: \"%.2f\", value))" }. Example in Kotlin: property(Product::price).map { value -> "$\${String.format(\"%.2f\", value)}" }.
Enumerable for type-safe enums
With enums, you can limit supported values for an argument or record field. To use an enum as an argument or record field, it must represent a primitive value (for example, String, Int) and conform to Enumerable.
Either types for union arguments
Either types act as a container for a value of one of a couple of types. They are useful when you want to pass various types for a single function argument. Three Either types are currently provided: Either<FirstType, SecondType> (A container for one of two types); EitherOfThree<FirstType, SecondType, ThirdType> (A container for one of three types); EitherOfFour<FirstType, SecondType, ThirdType, FourthType> (A container for one of four types).
ValueOrUndefined for distinguishing undefined from null (experimental)
ValueOrUndefined is an experimental wrapper type that allows you to distinguish between a JavaScript undefined value and an actual value. With regular optional types, both undefined and null from JavaScript are converted to null on the native side, making it impossible to tell them apart. ValueOrUndefined solves this by preserving the distinction. Properties: isUndefined (Returns true if the JavaScript value was undefined, false otherwise); optional (Returns the unwrapped value if present, or null if the value was undefined).
JavaScriptValue and JavaScript value types
It is possible to use a JavaScriptValue type which is a holder for any value that can be represented in JavaScript. This type is useful when you want to mutate the given argument or when you want to omit type validations and conversions. Note that using JavaScript-specific types is restricted to synchronous functions as all reads and writes in the JavaScript runtime must happen on the JavaScript thread. Any access to these values from different threads will result in a crash. In addition to the raw value, the JavaScriptObject type can be used to allow only object types and JavaScriptFunction<ReturnType> for callbacks.
Module native class
Module is a base class for a native module. Properties: appContext (AppContext) — Provides access to the AppContext. Methods: sendEvent(eventName: string, payload: Android: Map<String, Any?> | Bundle; iOS: [String: Any?]) — Sends an event with a given name and a payload to JavaScript.
AppContext native class
The app context is an interface to a single Expo app. Properties: constants (Android: ConstantsInterface? | iOS: EXConstantsInterface?) — Provides access to app's constants from legacy module registry; permissions (Android: Permissions? | iOS: EXPermissionsInterface?) — Provides access to the permissions manager from legacy module registry; activityProvider (ActivityProvider?, Android only) — Provides access to the activity provider from the legacy module registry; reactContext (Context?, Android only) — Provides access to the react application context; hasActiveReactInstance (Boolean, Android only) — Checks if there is an not-null, alive react native instance; utilities (EXUtilitiesInterface?, iOS only) — Provides access to the utilities from legacy module registry.
ExpoView native class
ExpoView is a base class that should be used by all exported views. On iOS, ExpoView extends RCTView which handles some styles (for example, borders) and accessibility. Properties: appContext (AppContext) — Provides access to the AppContext. To export your view using the View component, your custom class must inherit from ExpoView. You cannot change constructor parameters, because the provided view will be initialized by expo-modules-core.
View callbacks for view-bound events
Some events are connected to a certain view. For example, the touch event should be sent only to the underlying JavaScript view which was pressed. In that case, you cannot use sendEvent. The expo-modules-core introduces a view callbacks mechanism to handle view-bound events. In the view definition, provide the event names that the view can send using the Events definition component. Declare a property of type EventDispatcher in your view class. The name of the declared property must be the same as the name exported in the Events component. Later, you can call it as a function and pass a payload of type [String: Any?] on iOS and Map<String, Any?> on Android. To subscribe to these events in JavaScript/TypeScript, pass a function to the native view. Provided payload is available under the nativeEvent key.
Simple module example in Swift and Kotlin
Example in Swift: class MyModule: Module { public func definition() -> ModuleDefinition { Name("MyFirstExpoModule") Function("hello") { (name: String) in return "Hello \(name)!" } } }. Example in Kotlin: class MyModule : Module() { override fun definition() = ModuleDefinition { Name("MyFirstExpoModule") Function("hello") { name: String -> return "Hello $name!" } } }.
create-expo-module --full-example option
The --full-example option includes all available feature examples. This is equivalent to passing --features all.
add-platform-support --platform option
The --platform option selects the platforms to add to an existing module. Available values are apple, android, and web. In non-interactive mode, this option is required. In interactive mode, the command prompts you to choose from the platforms that are not already supported by the module. Example: npx create-expo-module@latest add-platform-support --platform android
add-platform-support --features option
The --features option overrides the feature examples used when generating files for the new platform when adding platform support. If the generated files do not match your module or no features are detected, pass --features explicitly. If no features are detected or provided, the command generates a minimal scaffold for the new platform. Example: npx create-expo-module@latest add-platform-support --platform android --features Function Event
add-platform-support --source option
The --source option uses a local template directory instead of downloading expo-module-template from npm when adding platform support.
create-expo-module template version selection
By default, create-expo-module downloads expo-module-template from npm. Standalone modules use the latest template. Local modules try to use the template version that matches the Expo SDK version installed in the current project, and fall back to the latest template when the SDK version cannot be detected. To test beta releases, set EXPO_BETA=1 before running the command: EXPO_BETA=1 npx create-expo-module@latest my-module