Chapitre 15.2 — Expo pour le web
Où on en est : cap sur la troisième cible que le template propose depuis le début (la touche
wde Metro !) : le navigateur. Que vaut vraiment « une app RN qui tourne en site » — surtout pour quelqu’un qui, comme vous, fait DÉJÀ du Next.js ?
⏱️ TL;DR — react-native-web traduit View→div, Text→span, StyleSheet→CSS : votre app Expo TOURNE dans le navigateur (
npx expo startpuisw, export statique possible, Expo Router fait même du vrai routing URL). Les limites : les modules NATIFS n’existent pas (SQLite, notifications, haptics — des pans entiers de Drill), le rendu n’est pas du « vrai web » (SEO/perf moyens, pas de SSR sérieux). Verdict pour vous : une curiosité utile (démo, prototype partageable) — pour un VRAI site, vous avez Next.js. Le pont intéressant est ailleurs : partager la LOGIQUE (lib/) entre l’app et un site.
🎯 Objectifs
- Lancer Drill dans le navigateur et comprendre ce qui marche/casse.
- Connaître la mécanique react-native-web (et ses limites structurelles).
- Trancher : quand la cible web d’Expo a du sens (et quand Next.js gagne).
- Voir le VRAI partage : lib/ commun entre app RN et site Next.js.
La démo en 30 secondes
npx expo install react-native-web react-dom
npx expo start
# … puis touche « w » : Drill s'ouvre dans le navigateurCe qui marche immédiatement : les écrans, la navigation (Expo Router mappe ses routes sur de vraies URLs — /themes/symfony navigable au clavier !), NativeWind (Tailwind redevient du CSS), Zustand, TanStack Query (le fetch GitHub passe — avec CORS cette fois, mais raw.githubusercontent l’autorise).
Ce qui casse — et pourquoi c’est structurel :
| Brique Drill | Au web | Raison |
|---|---|---|
| expo-sqlite | ❌ (ou wasm expérimental) | Pas de SQLite natif dans le navigateur |
| expo-notifications | ❌ | Les notifs web sont une AUTRE API |
| expo-haptics | ❌ silencieux | Pas de vibreur (ou API Vibration limitée) |
| Reanimated | ⚠️ partiel | Réimplémenté en CSS/JS — les worklets n’existent pas |
| EAS Update | ❌ inutile | Le web EST une OTA permanente (c’est une URL) |
Platform.OS === 'web' et les extensions .web.tsx permettent d’adapter — mais chaque ❌
du tableau exigerait une implémentation web parallèle (IndexedDB à la place de SQLite, la
Notification API…). Pour Drill : refaire la moitié de l’app.
Le verdict — spécialement pour un dev Next.js
La cible web d’Expo vise deux publics : les équipes 100 % RN qui veulent UNE présence web sans apprendre le web (légitime), et les démos/prototypes (un lien à partager sans APK). Vous n’êtes ni l’un ni l’autre : votre stack web (Next.js — RSC, SEO, l’écosystème DOM entier) est supérieure sur son terrain à tout ce que react-native-web produira.
La doctrine du cours : la touche w sert à prototyper et démontrer (montrer un écran à
quelqu’un via un lien ngrok, itérer du layout au grand écran) — jamais à livrer un site.
Le partage qui vaut de l’or : lib/
Le VRAI pont app/web n’est pas l’UI — c’est la logique. Notre discipline lib/ (zéro import react-native) rend ce partage trivial :
drill-core/ ← un package (pnpm workspace ou repo publié)
├── schemas.ts ← zod : Question, Manifest (ch. 7.3)
├── scoring.ts ← computePoints
├── draw.ts ← le tirage
└── answer-state.ts ← la table de vérité
drill/ (app Expo) → importe drill-core
drill-site/ (Next.js) → importe drill-core : le MÊME quiz, jouable en ligneScénario concret et séduisant : un mini-site Next.js « Drill web » qui fetch le MÊME repo drill-questions, avec la MÊME logique de score — la version web de démo qui donne envie d’installer l’app. Coût marginal : quasi nul, puisque la logique existe déjà, pure et testée (P14). C’est l’aboutissement de la règle posée au ch. 2.5 — et un pattern d’or pour toutes vos apps : le cœur en TS pur, les UI par plateforme.
⚠️ Piège — Si vous tentez l’aventure react-native-web quand même : le mode de sortie à utiliser est l’export statique d’Expo Router (
npx expo export --platform web) — un SPA/MPA hébergeable partout. Ne comparez pas son Lighthouse à un site Next.js : le bundle embarque un runtime RN entier — c’est une app portée, pas un site optimisé. Les attentes calibrées évitent la déception documentée de beaucoup d’équipes.
💡 Pour un dev Next.js — L’ironie complète du chapitre : React Native applique « learn once, write anywhere » — et pour VOUS, le « anywhere » web s’appelle déjà Next.js. Votre avantage compétitif n’est pas d’écrire l’UI une fois : c’est de savoir écrire LES DEUX UI nativement bien, au-dessus d’un cœur partagé. Peu de devs ont les deux moitiés — vous les avez désormais.
✏️ Exercices
1. Lancez Drill au web (touche w) et faites l’inventaire :. trois choses qui marchent mieux qu’attendu, trois cassées, une surprenante.
✅ Solution
Mieux qu’attendu (typique) : la navigation URL (deep links gratuits !), NativeWind (pixel- identique), les Layout Animations (retombées CSS correctes). Cassées : l’historique (SQLite), le défi quotidien (notifs), les haptics (silence). Surprenante : le clavier — TextInput devient un input DOM et le chapitre 3.5 entier devient sans objet (le navigateur gère). La séance d’inventaire vaut mieux que toute doc : chacun voit OÙ passe la ligne pour SON app.
2. Esquissez le découpage drill-core : listez ce qui de votre lib/ actuel peut y migrer tel quel, ce qui doit rester dans l’app, et le critère de tri.
✅ Solution
Migre : schemas.ts, scoring.ts, draw.ts, answer-state.ts, cn.ts — zéro dépendance. Reste :
db/ (expo-sqlite), notifications.ts, feedback.ts (expo-*), les hooks (React + libs RN).
Critère : « importable dans un script Node nu ? » — le test mécanique de pureté. Le
grep from 'expo / from 'react-native dans lib/ donne la réponse en 5 secondes (et doit
être vide sur la partie migrable — c’était notre règle).
3. Le pitch : en 4 lignes, justifiez (ou rejetez) « Drill web en Next.js » comme projet d’un week-end, avec l’architecture exacte.
✅ Solution
Justifié en démo/acquisition : Next.js App Router + drill-core (workspace) + fetch du même drill-questions + UI Tailwind reprenant le design system (les classes se copient quasi telles quelles de NativeWind !). Scores en localStorage (pas de SQLite nécessaire pour une démo). Hors périmètre assumé : notifs, offline poussé. Un week-end réaliste PARCE QUE le cœur (schémas, scoring, tirage) existe, testé — le projet est une UI, pas une app. (Et il ferait un excellent bac à sable avant Drill v2.)
🧠 Quiz
1. Que fait react-native-web mécaniquement ?
Réponse
Il implémente les primitives RN en DOM : View→div, Text→span, StyleSheet→CSS — le même code React rend dans le navigateur, Expo Router mappant ses routes sur de vraies URLs.
2. Citez trois briques de Drill sans équivalent web direct.
Réponse
expo-sqlite (pas de SQLite navigateur), expo-notifications (autre API web), expo-haptics (pas de vibreur) — et EAS Update perd son sens (une URL est déjà « OTA »).
3. Quels sont les deux usages légitimes de la cible web pour vous ?
Réponse
Prototyper/démontrer (un lien partageable, itérer au grand écran) — jamais livrer un site de production : Next.js est supérieur sur ce terrain.
4. Quel est le partage app/web qui vaut vraiment ?
Réponse
La logique pure (drill-core : schémas zod, scoring, tirage) — rendue partageable par la règle « lib/ sans react-native » du ch. 2.5. Le cœur en TS pur, les UI par plateforme.
5. Quel test mécanique dit qu’un module est partageable ?
Réponse
« Importable dans un script Node nu ? » — aucun import expo-*/react-native. Le grep de 5 secondes qui audite toute la lib/.
👉 Chapitre suivant : 15.3 — Modules natifs custom — écrire SON module Kotlin, le jour venu.