Expo Modules Core provides the foundational infrastructure for the Expo Modules architecture, enabling seamless integration between React Native applications and native platform code. It offers a comprehensive set of APIs including native module proxies for cross-platform communication, event emitters for real-time event handling, shared objects and references for efficient memory management, and permission handling utilities for secure access control.
npm install expo-modules-coreimport {
EventEmitter,
NativeModule,
SharedObject,
SharedRef,
Platform,
requireNativeModule,
requireOptionalNativeModule,
requireNativeViewManager,
registerWebModule,
uuid,
reloadAppAsync,
createSnapshotFriendlyRef,
useReleasingSharedObject,
createPermissionHook,
CodedError,
UnavailabilityError,
PermissionStatus,
type EventsMap,
type PermissionResponse,
type EventSubscription
} from "expo-modules-core";For CommonJS:
const {
EventEmitter,
NativeModule,
requireNativeModule,
requireOptionalNativeModule,
Platform,
uuid,
reloadAppAsync,
CodedError,
UnavailabilityError,
PermissionStatus
} = require("expo-modules-core");import {
EventEmitter,
requireNativeModule,
Platform,
type EventSubscription
} from "expo-modules-core";
// Create an event emitter for custom events
const emitter = new EventEmitter<{
dataReceived: (data: string) => void;
error: (error: Error) => void;
}>();
// Add event listeners
const subscription: EventSubscription = emitter.addListener("dataReceived", (data) => {
console.log("Received:", data);
});
// Emit events
emitter.emit("dataReceived", "Hello from native!");
// Load a native module
try {
const nativeModule = requireNativeModule("MyCustomModule");
const result = await nativeModule.performOperation();
} catch (error) {
console.log("Native module not available");
}
// Platform-specific code
const platformSpecificValue = Platform.select({
ios: "iOS implementation",
android: "Android implementation",
web: "Web implementation",
default: "Fallback implementation"
});
// Clean up
subscription.remove();Expo Modules Core is built around several key architectural components:
Native-backed event system for real-time communication between JavaScript and native code, with full TypeScript support and automatic lifecycle management.
class EventEmitter<TEventsMap extends EventsMap = Record<never, never>> {
addListener<EventName extends keyof TEventsMap>(
eventName: EventName,
listener: TEventsMap[EventName]
): EventSubscription;
emit<EventName extends keyof TEventsMap>(
eventName: EventName,
...args: Parameters<TEventsMap[EventName]>
): void;
}
interface EventSubscription {
remove(): void;
}Direct integration with native platform modules through JSI with automatic fallback to bridge proxy for backwards compatibility.
function requireNativeModule<ModuleType = any>(moduleName: string): ModuleType;
function requireOptionalNativeModule<ModuleType = any>(
moduleName: string
): ModuleType | null;
class NativeModule<TEventsMap extends EventsMap = Record<never, never>>
extends EventEmitter<TEventsMap> {
ViewPrototypes?: { [viewName: string]: object };
}Efficient memory sharing between JavaScript and native code with manual memory management capabilities for performance-critical scenarios.
class SharedObject<TEventsMap extends EventsMap = Record<never, never>>
extends EventEmitter<TEventsMap> {
release(): void;
}
class SharedRef<
TNativeRefType extends string = 'unknown',
TEventsMap extends EventsMap = Record<never, never>
> extends SharedObject<TEventsMap> {
nativeRefType: string;
}Cross-platform utilities for environment detection, platform-specific code execution, and browser capability checking.
interface Platform {
OS: string;
select: <T>(specifics: { [platform in PlatformSelectOSType]?: T }) => T;
isDOMAvailable: boolean;
canUseEventListeners: boolean;
canUseViewport: boolean;
isAsyncDebugging: boolean;
}
type PlatformSelectOSType = 'ios' | 'android' | 'web' | 'native' | 'electron' | 'default';React Hook-based permission system with standardized request/response patterns and automatic lifecycle management.
enum PermissionStatus {
GRANTED = 'granted',
UNDETERMINED = 'undetermined',
DENIED = 'denied'
}
interface PermissionResponse {
status: PermissionStatus;
expires: PermissionExpiration;
granted: boolean;
canAskAgain: boolean;
}
function createPermissionHook<Permission extends PermissionResponse, Options extends object>(
methods: PermissionHookMethods<Permission, Options>
): (options?: PermissionHookOptions<Options>) =>
[Permission | null, () => Promise<Permission>, () => Promise<Permission>];Integration system for React Native components backed by native view managers, with automatic prototype binding and lifecycle management.
function requireNativeViewManager<P>(
moduleName: string,
viewName?: string
): ComponentType<P>;Native-backed UUID generation with support for both random (v4) and deterministic (v5) UUID creation.
interface UUID {
v4(): string;
v5(name: string, namespace: string | number[]): string;
namespace: typeof Uuidv5Namespace;
}
enum Uuidv5Namespace {
dns = '6ba7b810-9dad-11d1-80b4-00c04fd430c8',
url = '6ba7b811-9dad-11d1-80b4-00c04fd430c8',
oid = '6ba7b812-9dad-11d1-80b4-00c04fd430c8',
x500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8'
}Standardized error classes with error codes for consistent error handling across Expo modules.
class CodedError extends Error {
code: string;
info?: any;
constructor(code: string, message: string);
}
class UnavailabilityError extends CodedError {
constructor(moduleName: string, propertyName: string);
}Additional utility functions for application lifecycle management, app reloading, and React development helpers.
function reloadAppAsync(reason?: string): Promise<void>;
function createSnapshotFriendlyRef<T>(): RefObject<T | null>;React hooks and utilities for seamless integration with React components and automatic resource management.
function useReleasingSharedObject<TSharedObject extends SharedObject>(
factory: () => TSharedObject,
dependencies: DependencyList
): TSharedObject;
function createSnapshotFriendlyRef<T>(): RefObject<T | null>;Deprecated event emitter class for backward compatibility with older modules.
/**
* @deprecated Use EventEmitter instead
*/
class LegacyEventEmitter {
constructor(nativeModule: any);
addListener<T>(eventName: string, listener: (event: T) => void): EventSubscription;
removeAllListeners(eventName: string): void;
removeSubscription(subscription: EventSubscription): void;
emit(eventName: string, ...params: any[]): void;
}Deprecated proxy object for accessing native modules.
/**
* @deprecated Use requireNativeModule or requireOptionalNativeModule instead
*/
const NativeModulesProxy: Record<string, ProxyNativeModule>;// Core event system types
type EventsMap = Record<string, (...args: any[]) => void>;
type PermissionExpiration = 'never' | number;
// Typed array support
type TypedArray = IntBasedTypedArray | UintBasedTypedArray | FloatBasedTypedArray;
type IntBasedTypedArray = Int8Array | Int16Array | Int32Array;
type UintBasedTypedArray = Uint8Array | Uint8ClampedArray | Uint16Array | Uint32Array;
type FloatBasedTypedArray = Float32Array | Float64Array;
// Platform types
type PlatformSelectOSType = 'ios' | 'android' | 'web' | 'native' | 'electron' | 'default';
// Legacy module proxy interface
interface ProxyNativeModule {
addListener?(eventName: string): void;
removeListeners?(count: number): void;
[key: string]: any;
}
// React integration types
type ComponentType<P = {}> = React.ComponentType<P>;
interface RefObject<T> {
readonly current: T | null;
}
type DependencyList = ReadonlyArray<any>;
// Permission system types
interface PermissionHookMethods<Permission extends PermissionResponse, Options = never> {
requestMethod: (options?: Options) => Promise<Permission>;
getMethod: (options?: Options) => Promise<Permission>;
}
interface PermissionHookOptions<Options extends object> extends Options {
get?: boolean;
request?: boolean;
}
// Global Expo namespace types
interface ViewConfig {
validAttributes: Record<string, any>;
directEventTypes: Record<string, { registrationName: string }>;
}
interface ExpoGlobal {
modules: Record<string, any>;
expoModulesCoreVersion?: { major: number; minor: number; patch: number };
cacheDir?: string;
documentsDir?: string;
uuidv4(): string;
uuidv5(name: string, namespace: string): string;
getViewConfig(moduleName: string, viewName?: string): ViewConfig | null;
reloadAppAsync(reason: string): Promise<void>;
}
declare global {
const expo: ExpoGlobal;
}