Écrire des modules Expo
Référence complète pour créer des modules natifs et des vues en utilisant l'API Expo Modules. Couvre Swift (iOS), Kotlin (Android) et TypeScript.
Quand l'utiliser
- Créer un nouveau module natif Expo ou une nouvelle vue native
- Ajouter une fonctionnalité native (caméra, capteurs, API système) à une application Expo
- Encapsuler des SDK de plateforme pour la consommation React Native
- Créer des config plugins qui modifient les fichiers de projet natifs
Références
Consultez ces ressources selon les besoins :
references/
native-module.md DSL de définition de module : Name, Function, AsyncFunction, Property, Constant, Events, système de types, objets partagés
native-view.md Composants de vue native : View, Prop, EventDispatcher, cycle de vie de la vue, fonctions basées sur ref
lifecycle.md Hooks de cycle de vie : module, app/AppDelegate iOS, listeners d'activité/application Android
config-plugin.md Config plugins : modification d'Info.plist, AndroidManifest.xml, lecture des valeurs dans le code natif
module-config.md Champs de expo-module.config.json et configuration d'autolinking
Démarrage rapide
Créer un module local (dans une application existante)
Toujours générer le scaffold avec create-expo-module d'abord, puis modifier le code généré. Cela garantit un podspec, build.gradle et config de module corrects — en évitant les erreurs de compilation courantes.
CI=1 npx create-expo-module@latest --local \
--name MyModule \
--description "My Expo module" \
--package expo.modules.mymodule
CI=1 ignore les invites interactives et utilise les flags fournis.
Important : En mode
CI=1(non-interactif), le scaffold crée toujours le répertoire sousmodules/my-module/car le slug est dérivé decustomTargetPathqui estundefinedpour les modules--local— le flag--namedéfinit uniquement le nom de la classe native, pas le répertoire. Après le scaffold, renommez-le en kebab-case correspondant à votre module (par ex.KeyValueStore→modules/key-value-store/), puis exécutezcd ios && pod installpour que CocoaPods utilise le bon chemin. Ignorer le renommage fonctionne, mais ignorerpod installaprès tout renommage provoque des erreurs de compilation iOS (« Build input file cannot be found »).
Flags disponibles :
| Flag | Description | Exemple |
|---|---|---|
--name |
Nom du module natif (PascalCase) | --name KeyValueStore |
--description |
Description du module | --description "Native key-value storage" |
--package |
Nom du package Android | --package expo.modules.keyvaluestore |
--author-name |
Nom de l'auteur | --author-name "dev" |
--author-email |
Email de l'auteur | --author-email "dev@example.com" |
--author-url |
URL du profil auteur | --author-url "https://github.com/dev" |
--repo |
URL du repository | --repo "https://github.com/dev/repo" |
Le scaffold génère à la fois un module natif (fonctions, events, constantes) et un composant de vue native (exemple WebView avec props et events). Après le scaffold :
- Décidez ce dont vous avez besoin : Si vous n'avez besoin que d'un module natif (pas d'UI), supprimez les fichiers de vue. Si vous n'avez besoin que d'une vue native, supprimez le boilerplate de fonction du module. Si vous avez besoin des deux, conservez les deux et remplacez les implémentations.
- Supprimez le boilerplate inutile : Le scaffold inclut du code d'exemple (fonction
hello(), constantePI, eventonChange, vue basée sur WebView avec propurl). Supprimez tout cela et remplacez-le par votre implémentation réelle. - Supprimez les fichiers web si non nécessaires : Le scaffold génère des fichiers
*.web.ts/*.web.tsxpour le support de la plateforme web. Supprimez-les si le module est natif uniquement. Supprimez aussi"web"du tableauplatformsdansexpo-module.config.json.
Éléments à supprimer pour un module uniquement (pas de vue native) :
- Supprimez
ios/MyModuleView.swift,android/.../MyModuleView.kt - Supprimez
src/MyModuleView.tsx,src/MyModuleView.web.tsx - Supprimez le bloc
View(...)de la définition du module en Swift et Kotlin - Supprimez les types liés à la vue de
MyModule.types.tset l'export de vue deindex.ts
Éléments à supprimer pour une vue uniquement (pas de fonctions de module) :
- Supprimez les blocs
Function,AsyncFunction,Constant,Eventsde la définition du module (conservezNameetView) - Simplifiez le fichier du module TypeScript pour exporter uniquement la vue
Structure générée (après renommage de my-module en kebab-case de votre module) :
modules/
my-module/ # Renommez en kebab-case, par ex. key-value-store/
android/
build.gradle
src/main/java/expo/modules/mymodule/
MyModule.kt # Définition du module (fonctions, events, enregistrement de vue)
MyModuleView.kt # Vue native (sous-classe ExpoView)
ios/
MyModule.podspec
MyModule.swift # Définition du module
MyModuleView.swift # Vue native (sous-classe ExpoView)
src/
MyModule.ts # Liaison du module natif
MyModule.web.ts # Implémentation web
MyModule.types.ts # Types partagés
MyModuleView.tsx # Composant de vue native
MyModuleView.web.tsx # Composant de vue web
expo-module.config.json
index.ts # Ré-exporte le module + la vue
Créer un module autonome (pour publication)
npx create-expo-module@latest my-module
Référence de la structure du module
Le DSL Swift et Kotlin partagent la même structure. Les deux plateformes sont affichées ici comme référence — dans d'autres fichiers de référence, Swift est montré comme langue principale sauf si le pattern Kotlin diffère significativement.
Swift (iOS) :
import ExpoModulesCore
public class MyModule: Module {
public func definition() -> ModuleDefinition {
Name("MyModule")
Function("hello") { (name: String) -> String in
return "Hello \(name)!"
}
}
}
Kotlin (Android) :
package expo.modules.mymodule
import expo.modules.kotlin.modules.Module
import expo.modules.kotlin.modules.ModuleDefinition
class MyModule : Module() {
override fun definition() = ModuleDefinition {
Name("MyModule")
Function("hello") { name: String ->
"Hello $name!"
}
}
}
TypeScript :
import { requireNativeModule } from "expo";
const MyModule = requireNativeModule("MyModule");
export function hello(name: string): string {
return MyModule.hello(name);
}
expo-module.config.json
{
"platforms": ["android", "apple"],
"apple": {
"modules": ["MyModule"]
},
"android": {
"modules": ["expo.modules.mymodule.MyModule"]
}
}
Note : iOS utilise juste le nom de la classe ; Android utilise le nom de classe complet (package + classe). Voir references/module-config.md pour tous les champs.