create-expo-module
Un cookbook para scaffolding de un módulo nativo local con una superficie TypeScript tipada - la ruta soportada cuando los paquetes de Expo SDK y las librerías de terceros no cubren tu integración.
Busca en todas las páginas de la documentación
Un cookbook para scaffolding de un módulo nativo local con una superficie TypeScript tipada - la ruta soportada cuando los paquetes de Expo SDK y las librerías de terceros no cubren tu integración.
Tarjeta de referencia rápida - lista para copiar y pegar.
# Desde la raíz de tu aplicación Expo
npx create-expo-module@latest modules/attestation
# Responde los prompts: nombre del módulo, descripción, plataformas (ios + android)
cd modules/attestation
npm install
npm run build # compila la superficie TypeScript
# Vincula a la aplicación (workspace o dependencia de archivo)
cd ../..
npm install ./modules/attestation
# Regenera los proyectos nativos
npx expo prebuild --clean
npx expo run:ios// Uso de la aplicación después del scaffold
import AttestationModule from "attestation";
const result = await AttestationModule.isDeviceTrustedAsync();Cuándo usar esto:
packages/attestation.npx create-expo-app@latest AttestationApp --template blank-typescript@sdk-57 --yes
cd AttestationApp
npx create-expo-module@latest modules/device-trustLayout de salida típico:
AttestationApp/
├── app.json
├── package.json
├── App.tsx
└── modules/
└── device-trust/
├── android/
│ └── src/main/java/expo/modules/devicetrust/DeviceTrustModule.kt
├── ios/
│ └── DeviceTrustModule.swift
├── src/
│ ├── DeviceTrustModule.ts
│ └── index.ts
├── expo-module.config.json
└── package.json// modules/device-trust/ios/DeviceTrustModule.swift
import ExpoModulesCore
public class DeviceTrustModule: Module {
public func definition() -> ModuleDefinition {
Name("DeviceTrust")
AsyncFunction("isDeviceTrustedAsync") { () -> Bool in
// Reemplaza con integración de DeviceCheck / App Attest
#if targetEnvironment(simulator)
return false
#else
return true
#endif
}
}
}// modules/device-trust/android/src/main/java/expo/modules/devicetrust/DeviceTrustModule.kt
package expo.modules.devicetrust
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class DeviceTrustModule : Module() {
override fun definition() = ModuleDefinition {
Name("DeviceTrust")
AsyncFunction("isDeviceTrustedAsync") {
// Reemplaza con Play Integrity API
!Build.FINGERPRINT.contains("generic")
}
}
}// modules/device-trust/src/DeviceTrustModule.ts
import { requireNativeModule } from "expo-modules-core";
type DeviceTrustModuleType = {
isDeviceTrustedAsync: () => Promise<boolean>;
};
export default requireNativeModule<DeviceTrustModuleType>("DeviceTrust");// modules/device-trust/src/index.ts
export { default } from "./DeviceTrustModule";// AttestationApp/package.json (fragmento)
{
"dependencies": {
"device-trust": "file:./modules/device-trust",
"expo": "~57.0.4",
"expo-dev-client": "~6.0.0",
"react": "19.2.3",
"react-native": "0.86.0"
}
}// App.tsx
import { useEffect, useState } from "react";
import { Button, StyleSheet, Text, View } from "react-native";
import DeviceTrust from "device-trust";
export default function App() {
const [trusted, setTrusted] = useState<boolean | null>(null);
useEffect(() => {
DeviceTrust.isDeviceTrustedAsync().then(setTrusted);
}, []);
return (
<View style={styles.container}>
<Text style={styles.title}>Device Trust Module</Text>
<Text>Trusted: {trusted === null ? "…" : trusted ? "sí" : "no"}</Text>
<Button title="Re-check" onPress={() => DeviceTrust.isDeviceTrustedAsync().then(setTrusted)} />
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, justifyContent: "center", padding: 24, gap: 8 },
title: { fontSize: 20, fontWeight: "700" },
});# Compila TypeScript del módulo, prebuild, ejecuta
cd modules/device-trust && npm run build && cd ../..
npx expo install expo-dev-client
npx expo prebuild
npx expo run:iosLo que esto demuestra:
create-expo-module genera esqueletos coincidentes de Swift/Kotlin/TypeScript con registro correcto de Name("DeviceTrust").requireNativeModule carga el binding JSI después de que el autolinking compila el código nativo.file:./modules/... dependencia mantiene el módulo local - publícalo después si se reutiliza en repositorios.Si el módulo necesita derechos, añade app.plugin.js dentro del paquete del módulo y regístralo en app.config.ts de la aplicación. Ve Config Plugins.
{
"platforms": ["ios", "android"],
"ios": {
"modules": ["DeviceTrustModule"]
},
"android": {
"modules": ["expo.modules.devicetrust.DeviceTrustModule"]
}
}El autolinking lee este archivo durante npx expo prebuild e introduce entradas Gradle/Pod - sin enlace manual en aplicaciones CNG.
| Layout | Ruta | Dependencia |
|---|---|---|
| Aplicación única | modules/device-trust | "file:./modules/device-trust" |
| Paquete de monorepo | packages/device-trust | "workspace:*" en package.json de la aplicación |
| npm publicado | registro externo | "device-trust": "^1.0.0" |
Para matices de autolinking en monorepo, ve Autolinking & expo-modules-core.
Expo Module (create-expo-module) | RN TurboModule (Codegen) |
|---|---|
| Autolinking de Expo + config plugins | RN-first; setup manual de Codegen |
DSL ModuleDefinition de Swift/Kotlin | C++/Java/ObjC con archivos de spec |
| Mejor para aplicaciones Expo SDK 57 | Mejor para librerías RN-core sin expo |
La mayoría de equipos de aplicaciones deberían comenzar con Expo Modules API. Elige Writing a TurboModule cuando compartas C++ entre plataformas o subes a la comunidad de RN.
Por Native Module Rules, documenta:
npm run build en el módulo - la aplicación importa salida build/ obsoleta. Solución: Añade "prepare": "expo-module build" o compila en CI antes de compilación de aplicación.Name("DeviceTrust") y requireNativeModule("DeviceTrust") - lanzamiento en tiempo de ejecución. Solución: Mantén nombres idénticos en Swift, Kotlin y TS.npx expo prebuild --clean.| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| Paquete Expo SDK | Característica existe upstream | API propietaria |
| Librería nativa npm de terceros | Puente de comunidad mantenido | No mantenido o sin New Arch |
| Expo Module (esta página) | Puente personalizado delgado en aplicación Expo | Núcleo C++ compartido pesado |
| TurboModule + Codegen | Librería de ecosistema RN | Integración estándar de aplicación Expo CNG |
Sí, en aplicaciones React Native desnudas con expo-modules-core instalado a través de npx install-expo-modules. Expo prebuild es todavía la ruta de cableado más fácil.
Sí. Usa workspace:* y habilita autolinkingModuleResolution - ve la página de autolinking.
Usa Events("onTrustChanged") en ModuleDefinition y EventEmitter en el lado de JS - el mismo patrón que expo-sensors.
Solo cuando el módulo requiere permisos, derechos, dependencias de Gradle o metadatos de manifest no cubiertos por valores predeterminados de autolinking.
Completa el scaffold, añade README y pruebas, ejecuta npm publish desde modules/device-trust. Los consumidores npx expo install device-trust y registran cualquier plugin.
Versiones de stack: Esta página fue escrita para React 19.2.3, React Native 0.86.0 y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 16 jul 2026