Web vers natif
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 peu à peu : créer une enveloppe native, lancer toute l'UI web dedans dès le jour un, puis étouffer chaque écran pour le rendre natif par ordre de priorité. Cette skill ordonne le travail ; chaque étape se transmet à une skill Expo existante plutôt que de la ré-expliquer. Elle opérationnalise From Web to Native with React d'Expo — lis-le pour comprendre le pourquoi.
flowchart TD
A1[1 · Évaluer : rédiger la liste de travail] --> A2[2 · Échafauder l'enveloppe Expo]
A2 --> A3[3 · Enveloppe composants DOM<br/>· use-dom · JOUR UN]
A3 --> A4[4 · Étouffer les écrans en natif<br/>priorité la plus élevée · building-native-ui]
A4 -->|plus d'écrans| A4
A4 --> A5[5 · Connecter données / auth / stockage<br/>· native-data-fetching]
A5 --> A6[6 · Lancer · expo-deployment]
Principes
- Migrer, ne pas réécrire. Ne jamais tout faire d'un coup ; chaque étape garde l'app en état de lancer.
- Lancer le jour un. L'UI web s'exécute dans une enveloppe composants DOM (étape 3) avant que rien ne soit nativifié — c'est le jalon ; tout ce qui suit est du polissage.
- Étouffer par valeur. Nativifier les écrans hot ; laisser le reste dans la webview. Chaque écran DOM porte un runtime web d'environ 2 MB — raison suffisante de ne pas tout lancer en DOM.
- Nativifier signifie redesigner, pas reskin. Un écran étouffé devrait ressembler à ce qu'Apple/Google aurait livré, pas à la page web restylisée. Utilise d'abord
@expo/ui— il affiche du vrai SwiftUI/Compose, donc c'est exactement comme l'OS ; les primitives RN stylisées sont le fallback pour les layouts custom seulement. Plus la navigation par plateforme (building-native-ui: NativeTabs, large titles), le verre liquide et les composants natifs via@expo/ui, et l'UX mobile (sheets, swipe, haptics). La carte des motifs web→natif est dans./references/native-patterns.md. Si ça ressemble toujours à un site, tu as porté au lieu de redesigner. - Vérifier en exécutant, pas en compilant. Un build propre ne prouve rien (une webview vide compile très bien). Exécute chaque écran — mais juge le contenu et le comportement par rapport à l'original web, pas les pixels (un écran nativifié devrait ressembler plus au natif, pas identique).
- Orchestrer, ne pas réinventer. Chaque étape bascule vers une skill existante. La valeur ici, c'est l'ordre et les pièges — les mappings idiome-par-idiome vivent dans
./references/false-friends.md.
L'exécuter comme une boucle (recommandé)
La migration est une longue boucle repeat-until-done, donc le premier geste est de rédiger l'objectif global et le lancer — pas de moudre 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 de travail et pilote l'écran suivant (il auto-amorce même l'étape d'évaluation). Ensuite lance /goal avec lui — ou, si le harness ne peut pas boucler, écris-le dans migration-goal.md et fais lancer l'utilisateur. Les étapes ci-dessous sont ce que chaque itération fait ; exécute-les à la main seulement si tu ne boucles pas.
La migration
Pas de repo à migrer — tu construis juste du natif frais en tant que dev web ? Tu n'as pas besoin de ces étapes : utilise
building-native-ui, et garde./references/false-friends.mdouvert pour la carte des idiomes web→natif. Tout ce qui suit suppose une app web existante.
1. Évaluer → rédiger la liste de travail
Lis le repo et produis migration-progress.md, la liste de travail durable que le reste de la migration coche. Fais deux coupures :
- Écrans vs backend. Les routes de page (
page.tsx) sont des écrans que tu migres ; les routes serveur (route.ts), l'ORM, et les handlers d'auth restent côté serveur. Décide le backend une fois : le garder déployé (l'app native devient un client HTTP) ou le déplacer vers EAS Hosting (expo-api-routes). - Classe chaque écran selon comment il devrait arriver : port-as-is (présentatif → lancer dans une webview DOM), nativify-now (hot, ou a besoin du ressenti natif — gestes, listes, clavier), nativify-later, ou hybrid (une enveloppe native autour d'une sous-arborescence web, ex. une liste de chat enrobant un renderer markdown).
Note les signaux de framework au fur et à mesure que tu lis — RSC vs client, Tailwind/shadcn, où les données sont récupérées — puisqu'ils décident comment chaque écran se porte (false-friends a les mappings ; les Server Components async en particulier doivent être scindés en un fetch client + un composant présentatif avant de pouvoir bouger). Signale aussi les services/SDKs tiers — les SDKs navigateur ne se reportent pas (false-friends → Services & SDKs) ; les paiements surtout c'est un fork, pas un swap (les biens numériques in-app doivent utiliser l'IAP du magasin via RevenueCat, ~30% — pas Stripe), un appel de modèle économique à faire maintenant, pas à l'examen de l'App Store. La liste de travail n'est fiable qu'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 de Next se mappe quasi 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 un
Apporte chaque écran en tant que composant DOM ('use dom', selon la skill use-dom) rendu par sa route native, pour que l'app entière s'exécute sur téléphone avant que rien ne soit nativifié. Attends-toi à des édits par écran — dépacker les Server Components, swapper les imports de framework (next/link), porter le styling — tout couvert dans false-friends. Puis vérifie en exécutant (ci-dessous) ; c'est lanç-able sur TestFlight tel quel.
4. Étouffer les écrans en natif — par valeur
Parcours migration-progress.md de haut en bas. Pour chaque écran, redesigne-le natif — ne porte pas le layout web. Utilise d'abord @expo/ui (vrai SwiftUI/Compose — boutons, listes, sheets, pickers, sliders ; ./references/native-patterns.md mappe quel motif web devient quel composant natif), puis la navigation par plateforme (building-native-ui — NativeTabs, large titles) et l'UX mobile (swipe, haptics, momentum/inverted scroll) ; les primitives RN seulement pour les layouts custom. Consulte ./references/false-friends.md pour chaque idiome. @expo/ui et les composants DOM s'exécutent tous deux dans Expo Go (SDK 56+) — un dev build (la skill expo-dev-client) n'est nécessaire que pour les modules natifs custom. Vérifie le contenu et le comportement contre l'original web en exécution (le look devrait devenir plus natif), puis coche-le. Un écran par passe, app lanç-able partout. C'est une boucle sur une liste de travail durable, donc elle peut s'exécuter sans surveillance — confie-la à 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éménagement — les récupérations relatives, les sessions par cookie, localStorage, et les vars env changent tous (swaps dans false-friends). Utilise native-data-fetching pour les requêtes et le caching ; ajoute expo-api-routes si le backend a déménagé vers EAS Hosting.
6. Lancer
expo-deployment pour les builds du magasin (App Store / Play / TestFlight), EAS Update pour les poussées OTA après.
Vérifier en exécutant, pas en compilant
Un expo export vert prouve qu'un écran se compile, pas qu'il s'affiche — un écran peut builder et s'afficher vide ou mal s'afficher. Donc après l'enveloppe et après chaque écran nativifié, 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'arbre d'accessibilité,screenshot. - Natif — pilote le simulateur avec
argent:describe/debugger-component-treepour la structure,flowpour rejouer la vérification à chaque passe.
Passe sur la parité du contenu et du comportement — pas les pixels : un écran nativifié devrait ressembler plus au natif que le web, jamais identique (l'étape enveloppe-DOM est l'exception — là c'est l'UI web, donc ça devrait matcher). La sensation fait partie du natif et ne peut pas être capturée en screenshot — pour les écrans avec transitions ou gestes, capture un court enregistrement, pas juste un still (voir native-patterns.md → Feel). Cette boucle est d'opinion sur ses outils : si agent-browser ou argent n'est pas installé, demande à l'utilisateur et l'installe avant de continuer — ne 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 chacun. La lookup pour les étapes 3–5, et pour tout dev web en réapprentissage d'idiomes../references/native-patterns.md— motif UX web → redesign natif (@expo/ui-first). Le playbook de redesign étape-4 pour que les écrans ressemblent au natif d'OS, pas reskinné../references/verify-on-device.md— la recette parité deux-agents : pilote l'app web (browser agent) et l'app native (argent), ouvre la même route, compare../references/run-as-goal.md— un objectif global prêt à l'emploi, spécifique migration, 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.