Web to Native
Une app React web ne se convertit pas en natif — il n'existe pas de transpileur. Elle migre, écran par écran, comme un figuier étrangleur qui pousse autour d'un arbre et le remplace lentement : mettre en place une enveloppe native, exécuter toute l'interface web à l'intérieur dès le jour 1, puis étouffer chaque écran en natif par ordre de priorité. Cette skill est la colonne vertébrale qui ordonne le travail ; chaque étape bascule vers une skill Expo existante plutôt que de la réexpliquer. Elle opérationnalise le guide From Web to Native with React d'Expo — lis-le pour comprendre le pourquoi.
flowchart TD
A1[1 · Évaluer : rédiger la liste des tâches] --> A2[2 · Échafauder l'enveloppe Expo]
A2 --> A3[3 · Enveloppe composants DOM<br/>· expo-dom · LIVRER LE JOUR 1]
A3 --> A4[4 · Étouffer les écrans en natif<br/>valeur maximale d'abord · expo-router]
A4 -->|plus d'écrans| A4
A4 --> A5[5 · Connecter données / auth / stockage<br/>· expo-data-fetching]
A5 --> A6[6 · Livrer · eas-app-stores]
Principes
- Migrer, ne pas réécrire. Ne jamais le faire d'un seul coup ; chaque étape garde l'app livrable.
- Livrer le jour 1. L'interface web s'exécute dans une enveloppe composants DOM (étape 3) avant toute nativisation — c'est le jalon ; tout le reste est un raffinement.
- Étouffer par valeur. Nativiser les écrans chauds ; laisser le reste dans la webview. Chaque écran DOM porte un runtime web de ~2 MB — raison suffisante de ne pas tout livrer en DOM.
- Nativiser signifie redesigner, pas relooker. Un écran étranglé devrait ressembler à quelque chose qu'Apple/Google aurait livré, pas à la page web relookée. Atteins d'abord
@expo/ui— il rend du vrai SwiftUI/Compose, donc ça ressemble exactement à l'OS ; les primitives RN stylisées sont le recours pour les layouts personnalisés uniquement. Plus la navigation de plateforme (expo-router: NativeTabs, grands titres), verre liquide et composants natifs via@expo/ui, et UX mobile (sheets, swipes, haptics). La carte de modèles web→natif se trouve dans./references/native-patterns.md. Si ça ressemble toujours à un site web, tu as porté au lieu de redesigner. - Vérifier en exécutant, pas en compilant. Une build propre ne prouve rien (une webview vide compile bien). Exécute chaque écran — mais juge contenu et comportement par rapport à l'original web, pas pixels (un écran nativé devrait ressembler plus à du natif, pas identique).
- Orchestrer, ne pas réinventer. Chaque étape bascule vers une skill existante. La valeur ici est l'ordre et les pièges — les mappings idiome-par-idiome se trouvent dans
./references/false-friends.md.
L'exécuter en boucle (recommandé)
La migration est une longue boucle répéter-jusqu'à-fini, donc le premier mouvement est de rédiger l'objectif cible et le lancer — non pas de traiter les écrans à la main. Remplis l'objectif dans ./references/run-as-goal.md pour cette app et présente-le ; il relit cette skill à chaque itération, donc chaque tour /goal recharge le playbook + la liste des tâches et pilote l'écran suivant (il amorce même l'étape d'évaluation). Puis exécute /goal avec — ou, si le harness ne peut pas boucler, écris-le dans migration-goal.md et demande à l'utilisateur de le lancer. Les étapes ci-dessous sont ce que chaque itération fait ; exécute-les à la main uniquement si tu ne boucles pas.
La migration
Pas de repo à migrer - tu construis du natif fresh en tant que web dev ? Tu n'as pas besoin de ces étapes : utilise
expo-router, et garde./references/false-friends.mdouvert pour la carte idiomatique web→natif. Tout ce qui suit suppose une app web existante.
1. Évaluer → rédiger la liste des tâches
Lis le repo et produis migration-progress.md, la liste durable des tâches que le reste de la migration coche. Fais deux tranches :
- Écrans vs backend. Les routes de page (
page.tsx) sont des écrans que tu migres ; les routes serveur (route.ts), l'ORM et les gestionnaires d'auth restent côté serveur. Décide du backend une fois : le garde déployé (l'app native devient un client HTTP) ou le déplace vers EAS Hosting (eas-hosting). - Classe chaque écran par comment il devrait atterrir : port-tel-quel (présentatif → livré dans une webview DOM), nativiser-maintenant (chaud, ou a besoin d'une ressenti natif — gestes, listes, clavier), nativiser-plus-tard, ou hybride (une enveloppe native autour d'une sous-arborescence web, p. ex. une liste de chat enrobant un rendu markdown).
Note les signaux du framework en lisant — RSC vs client, Tailwind/shadcn, où les données sont récupérées — puisqu'ils décident comment chaque écran porte (false-friends a les mappings ; les Server Components asynchrones en particulier doivent être divisés en fetch client + composant présentatif avant de pouvoir bouger). Signale aussi les services/SDKs tiers — les SDKs navigateur ne passent pas (false-friends → Services & SDKs) ; les paiements surtout c'est un fork, pas un swap (les biens numériques in-app doivent utiliser IAP via store via RevenueCat, ~30% — pas Stripe), un choix de modèle métier à prendre maintenant, pas à la revue App Store. La liste des tâches est seulement fiable une fois que chaque route est triée et chaque écran classé.
2. Échafauder l'enveloppe
create-expo-app, puis refléter les routes web dans Expo Router — l'arborescence Next mappe presque 1:1 (note [id]/page.tsx → [id].tsx, et les routes peuvent vivre dans src/app/). Écrans vides, un par route.
3. L'envelopper en composants DOM — le jalon du jour 1
Apporte chaque écran en tant que composant DOM ('use dom', par la skill expo-dom) rendu par sa route native, afin que l'app entière s'exécute sur un téléphone avant toute nativisation. Attends-toi à des édits par écran - déballage des Server Components, swap des imports de framework (next/link), portage du styling — tout couvert dans false-friends. Puis vérifie en exécutant (ci-dessous) ; c'est livrable à TestFlight tel quel.
4. Étouffer les écrans en natif — par valeur
Parcours migration-progress.md de haut en bas. Pour chaque écran, redesigne-le en natif - ne porte pas le layout web. Atteins @expo/ui d'abord (vrai SwiftUI/Compose - boutons, listes, sheets, pickers, sliders ; ./references/native-patterns.md mappe quel modèle web devient quel composant natif), puis navigation de plateforme (expo-router - NativeTabs, grands titres) et UX mobile (swipes, haptics, momentum/inverted scroll) ; primitives RN seulement pour les layouts personnalisés. Consulte ./references/false-friends.md pour chaque idiome. @expo/ui et les composants DOM s'exécutent tous deux dans Expo Go (SDK 56+) - une dev build (la skill expo-dev-client) est seulement nécessaire pour les modules natifs personnalisés. Vérifie contenu et comportement par rapport à l'original web en exécution (le look devrait devenir plus natif), puis coche-le. Un écran par passage, app livrable partout. C'est une boucle sur une liste durable, donc elle peut tourner sans surveillance - bascule-la vers une boucle goal (./references/run-as-goal.md).
5. Connecter données, auth et stockage
La couche données web ne survit pas au déplacement - les requêtes relatives, les sessions par cookies, localStorage et les env vars changent tous (swaps dans false-friends). Utilise expo-data-fetching pour les requêtes et la mise en cache ; ajoute eas-hosting si le backend a déménagé vers EAS Hosting.
6. Livrer
eas-app-stores pour les builds store (App Store / Play / TestFlight), EAS Update pour les OTA pushes après.
Vérifier en exécutant, pas en compilant
Un expo export vert prouve qu'un écran assemble, pas qu'il rend — un écran peut compiler et toujours rendre vide ou mal rendre. Donc après l'enveloppe et après chaque écran nativé, compare les deux apps en exécution pour la même route :
- Original web — capture-le avec
agent-browser(CLI vercel-labs) :openla route,snapshot --jsonl'arborescence d'accessibilité,screenshot. - Natif — pilote le simulateur avec
argent:describe/debugger-component-treepour la structure,flowpour rejouer la vérification à chaque passage.
Valide sur la parité de contenu et comportement — pas pixels : un écran nativé devrait ressembler plus natif que le web, jamais identique (l'étape enveloppe DOM est l'exception — là c'est l'interface web, donc ça devrait correspondre). Le ressenti fait partie du natif et ne peut pas être capturé en screenshot — pour les écrans avec transitions ou gestes, capture une courte vidéo, pas juste un still (voir native-patterns.md → Feel). Cette boucle est partisane de son outillage : si agent-browser ou argent n'est pas installé, demande à l'utilisateur et installe avant de procéder — ne te replie pas sur des screenshots manuels. Recette complète et setup dans ./references/verify-on-device.md.
Références
./references/false-friends.md— idiome web → équivalent natif + le piège pour chaque. Le lookup pour les étapes 3–5, et pour tout web dev réapprenant des idiomes../references/native-patterns.md— modèle pattern UX web → redesign natif (@expo/ui-d'abord). Le playbook redesign étape 4 afin que les écrans ressemblent à du natif OS, pas du relooké../references/verify-on-device.md— la recette parité deux-agents : pilote l'app web (agent navigateur) et l'app native (argent), ouvre la même route, compare../references/run-as-goal.md— un objectif goal migration-spécifique tout formé pour piloter l'étape 4 sans surveillance (relit cette skill à chaque itération).- Expo — From Web to Native with React — le guide canonique que cette skill opérationnalise.