[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-expo-expo-web-to-native":3,"mdc-b5cdk8-key":37,"related-org-expo-expo-web-to-native":1149,"related-repo-expo-expo-web-to-native":1318},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":26,"repoUrl":27,"updatedAt":28,"license":29,"forks":30,"topics":31,"repo":32,"sourceUrl":35,"mdContent":36},"expo-web-to-native","migrate web React apps to Expo","Framework (OSS). Migrate an existing web React app to a native iOS\u002FAndroid app with Expo. Use when the user wants to turn a website into a mobile app, port a Next.js\u002FVite\u002FCRA React codebase to React Native, reuse web code on native incrementally, or asks how web idioms (the DOM, CSS, React Router, localStorage, window) map to native. This is the end-to-end migration guide; use the `expo-dom` skill for the DOM-component mechanism itself.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"expo","Expo","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fexpo.png",[12,16,19,22,23],{"name":13,"slug":14,"type":15},"Modernization","modernization","tag",{"name":17,"slug":18,"type":15},"React Native","react-native",{"name":20,"slug":21,"type":15},"React","react",{"name":9,"slug":8,"type":15},{"name":24,"slug":25,"type":15},"Mobile","mobile",2190,"https:\u002F\u002Fgithub.com\u002Fexpo\u002Fskills","2026-07-24T05:36:38.195043","MIT",111,[],{"repoUrl":27,"stars":26,"forks":30,"topics":33,"description":34},[],"A collection of AI agent skills for working with Expo projects and Expo Application Services","https:\u002F\u002Fgithub.com\u002Fexpo\u002Fskills\u002Ftree\u002FHEAD\u002Fplugins\u002Fexpo\u002Fskills\u002Fexpo-web-to-native","---\nname: expo-web-to-native\ndescription: Framework (OSS). Migrate an existing web React app to a native iOS\u002FAndroid app with Expo. Use when the user wants to turn a website into a mobile app, port a Next.js\u002FVite\u002FCRA React codebase to React Native, reuse web code on native incrementally, or asks how web idioms (the DOM, CSS, React Router, localStorage, window) map to native. This is the end-to-end migration guide; use the `expo-dom` skill for the DOM-component mechanism itself.\nversion: 1.0.0\nlicense: MIT\n---\n\n# Web to Native\n\nA web React app does not *convert* to native — there is no transpiler. It **migrates**, screen by screen, the way a strangler fig grows around a tree and slowly replaces it: stand up a native shell, run the whole web UI inside it on day one, then strangle each screen into native in priority order. This skill is the spine that orders the work; each step hands off to an existing Expo skill rather than re-explaining it. It operationalizes Expo's [From Web to Native with React](https:\u002F\u002Fexpo.dev\u002Fblog\u002Ffrom-web-to-native-with-react) — read that for the why.\n\n```mermaid\nflowchart TD\n    A1[1 · Assess: write the worklist] --> A2[2 · Scaffold Expo shell]\n    A2 --> A3[3 · DOM-component shell\u003Cbr\u002F>· expo-dom · SHIP DAY ONE]\n    A3 --> A4[4 · Strangle screens to native\u003Cbr\u002F>highest-value first · expo-router]\n    A4 -->|more screens| A4\n    A4 --> A5[5 · Wire data \u002F auth \u002F storage\u003Cbr\u002F>· expo-data-fetching]\n    A5 --> A6[6 · Ship · eas-app-stores]\n```\n\n## Principles\n\n- **Migrate, don't rewrite.** Never big-bang it; every step keeps the app shippable.\n- **Ship on day one.** The web UI runs in a DOM-component shell (step 3) before anything is nativized — that's the milestone; everything after is polish.\n- **Strangle by value.** Nativize the hot screens; leave the rest in the webview. Each DOM screen carries a ~2 MB web runtime — reason enough not to ship everything as DOM.\n- **Nativize means redesign, not reskin.** A strangled screen should look like Apple\u002FGoogle shipped it, not the web page reskinned. **Reach for `@expo\u002Fui` first** - it renders real SwiftUI\u002FCompose, so it feels *exactly* like the OS; styled RN primitives are the fallback for custom layouts only. Plus platform navigation (`expo-router`: NativeTabs, large titles), liquid glass and native components via `@expo\u002Fui`, and mobile UX (sheets, swipe, haptics). The web→native pattern map is [`.\u002Freferences\u002Fnative-patterns.md`](.\u002Freferences\u002Fnative-patterns.md). If it still feels like a website, you ported instead of redesigned.\n- **Verify by running, not compiling.** A clean build proves nothing (a blank webview compiles fine). Run each screen — but judge *content and behavior* against the web original, not pixels (a nativized screen should look more native, not identical).\n- **Orchestrate, don't reinvent.** Each step routes into an existing skill. The value here is the *order* and the *gotchas* — the idiom-by-idiom mappings live in [`.\u002Freferences\u002Ffalse-friends.md`](.\u002Freferences\u002Ffalse-friends.md).\n\n## Run it as a loop (recommended)\n\nThe migration is a long repeat-until-done loop, so the first move is to **write the goal objective and launch it** — not to grind screens by hand. Fill the objective in [`.\u002Freferences\u002Frun-as-goal.md`](.\u002Freferences\u002Frun-as-goal.md) for this app and present it; it **re-reads this skill every iteration**, so each `\u002Fgoal` turn reloads the playbook + worklist and drives the next screen (it even self-bootstraps the assess step). Then run `\u002Fgoal` with it — or, if the harness can't loop, write it to `migration-goal.md` and have the user launch it. The steps below are what each iteration does; run them by hand only if you're not looping.\n\n## The migration\n\n> **No repo to migrate** - just building native fresh as a web dev? You don't need these steps: use `expo-router`, and keep [`.\u002Freferences\u002Ffalse-friends.md`](.\u002Freferences\u002Ffalse-friends.md) open for the web→native idiom map. Everything below assumes an existing web app.\n\n### 1. Assess → write the worklist\n\nRead the repo and produce `migration-progress.md`, the durable worklist the rest of the migration checks off. Make two cuts:\n\n- **Screens vs backend.** Page routes (`page.tsx`) are screens you migrate; server routes (`route.ts`), the ORM, and auth handlers stay server-side. Decide the backend once: keep it deployed (the native app becomes an HTTP client) or move it to EAS Hosting (`eas-hosting`).\n- **Bucket each screen** by how it should land: **port-as-is** (presentational → ships in a DOM webview), **nativize-now** (hot, or needs native feel — gestures, lists, keyboard), **nativize-later**, or **hybrid** (a native shell around a web sub-tree, e.g. a chat list wrapping a markdown renderer).\n\nNote the framework signals as you read — RSC vs client, Tailwind\u002Fshadcn, where data is fetched — since they decide how each screen ports (false-friends has the mappings; async Server Components in particular must be split into a client fetch + a presentational component before they can move). **Flag third-party services\u002FSDKs too** — browser SDKs don't carry over (`false-friends` → *Services & SDKs*); payments especially is a *fork, not a swap* (in-app digital goods must use store IAP via RevenueCat, ~30% — not Stripe), a business-model call to make now, not at App Store review. The worklist is only trustworthy once every route is sorted and every screen bucketed.\n\n### 2. Scaffold the shell\n\n`create-expo-app`, then mirror the web routes in Expo Router — Next's tree maps almost 1:1 (note `[id]\u002Fpage.tsx` → `[id].tsx`, and routes may live in `src\u002Fapp\u002F`). Empty screens, one per route.\n\n### 3. Shell it in DOM components — the day-one milestone\n\nBring every screen over as a DOM component (`'use dom'`, per the `expo-dom` skill) rendered by its native route, so the whole app runs on a phone before anything is nativized. Expect per-screen edits - unwrapping Server Components, swapping framework imports (`next\u002Flink`), carrying the styling over - all covered in false-friends. Then verify by running (below); this is shippable to TestFlight as-is.\n\n### 4. Strangle screens to native — by value\n\nWalk `migration-progress.md` top-down. For each screen, *redesign* it native - don't port the web layout. Reach for **`@expo\u002Fui` first** (real SwiftUI\u002FCompose - buttons, lists, sheets, pickers, sliders; [`.\u002Freferences\u002Fnative-patterns.md`](.\u002Freferences\u002Fnative-patterns.md) maps which web pattern becomes which native component), then platform navigation (`expo-router` - NativeTabs, large titles) and mobile UX (swipe, haptics, momentum\u002Finverted scroll); RN primitives only for custom layouts. Consult [`.\u002Freferences\u002Ffalse-friends.md`](.\u002Freferences\u002Ffalse-friends.md) for each idiom. `@expo\u002Fui` and DOM components both run in **Expo Go** (SDK 56+) - a dev build (the `expo-dev-client` skill) is only needed for *custom* native modules. Verify *content and behavior* against the running web original (the look should become more native), then check it off. One screen per pass, app shippable throughout. It's a loop over a durable worklist, so it can run unattended - hand it to a goal loop ([`.\u002Freferences\u002Frun-as-goal.md`](.\u002Freferences\u002Frun-as-goal.md)).\n\n### 5. Wire data, auth, and storage\n\nThe web data layer doesn't survive the move - relative fetches, cookie sessions, `localStorage`, and env vars all change (swaps in false-friends). Use `expo-data-fetching` for requests and caching; add `eas-hosting` if the backend moved to EAS Hosting.\n\n### 6. Ship\n\n`eas-app-stores` for the store builds (App Store \u002F Play \u002F TestFlight), EAS Update for OTA pushes after.\n\n## Verify by running, not compiling\n\nA green `expo export` proves a screen *bundles*, not that it *renders* — a screen can build and still render blank or mis-render. So after the shell and after every nativized screen, compare the two **running** apps for the same route:\n\n- **Web original** — capture it with **`agent-browser`** (vercel-labs CLI): `open` the route, `snapshot --json` the accessibility tree, `screenshot`.\n- **Native** — drive the simulator with **`argent`**: `describe` \u002F `debugger-component-tree` for structure, `flow` to replay the check each pass.\n\nPass on parity of **content and behavior** — not pixels: a nativized screen should look *more* native than the web, never identical (the DOM-shell stage is the exception — there it *is* the web UI, so it should match). Feel is part of native and can't be screenshotted — for screens with transitions or gestures, capture a short recording, not just a still (see `native-patterns.md` → Feel). This loop is **opinionated about its tooling**: if `agent-browser` or `argent` isn't installed, ask the user and install it before proceeding — don't fall back to manual screenshots. Full recipe and setup in [`.\u002Freferences\u002Fverify-on-device.md`](.\u002Freferences\u002Fverify-on-device.md).\n\n## References\n\n- [`.\u002Freferences\u002Ffalse-friends.md`](.\u002Freferences\u002Ffalse-friends.md) — web idiom → native equivalent + the gotcha for each. The lookup for steps 3–5, and for any web dev unlearning idioms.\n- [`.\u002Freferences\u002Fnative-patterns.md`](.\u002Freferences\u002Fnative-patterns.md) — web UX *pattern* → native redesign (`@expo\u002Fui`-first). The step-4 redesign playbook so screens feel OS-native, not reskinned.\n- [`.\u002Freferences\u002Fverify-on-device.md`](.\u002Freferences\u002Fverify-on-device.md) — the two-agent parity recipe: drive the web app (browser agent) and the native app (argent), open the same route, compare.\n- [`.\u002Freferences\u002Frun-as-goal.md`](.\u002Freferences\u002Frun-as-goal.md) — a ready-shaped, migration-specific goal objective for driving step 4 unattended (re-reads this skill each iteration).\n- [Expo — From Web to Native with React](https:\u002F\u002Fexpo.dev\u002Fblog\u002Ffrom-web-to-native-with-react) — the canonical guide this skill operationalizes.\n\n## Submitting Feedback\nIf you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:\n```bash\nnpx --yes submit-expo-feedback@latest --category skills --subject \"expo-web-to-native\" \"\u003Cactionable feedback>\"\n```\nOnly submit when you have something specific and actionable to report. Include as much relevant context as possible.\n",{"data":38,"body":40},{"name":4,"description":6,"version":39,"license":29},"1.0.0",{"type":41,"children":42},"root",[43,52,85,162,169,314,320,373,379,410,417,430,505,539,545,579,585,614,620,721,727,755,761,772,778,812,904,968,974,1054,1060,1065,1138,1143],{"type":44,"tag":45,"props":46,"children":48},"element","h1",{"id":47},"web-to-native",[49],{"type":50,"value":51},"text","Web to Native",{"type":44,"tag":53,"props":54,"children":55},"p",{},[56,58,64,66,72,74,83],{"type":50,"value":57},"A web React app does not ",{"type":44,"tag":59,"props":60,"children":61},"em",{},[62],{"type":50,"value":63},"convert",{"type":50,"value":65}," to native — there is no transpiler. It ",{"type":44,"tag":67,"props":68,"children":69},"strong",{},[70],{"type":50,"value":71},"migrates",{"type":50,"value":73},", screen by screen, the way a strangler fig grows around a tree and slowly replaces it: stand up a native shell, run the whole web UI inside it on day one, then strangle each screen into native in priority order. This skill is the spine that orders the work; each step hands off to an existing Expo skill rather than re-explaining it. It operationalizes Expo's ",{"type":44,"tag":75,"props":76,"children":80},"a",{"href":77,"rel":78},"https:\u002F\u002Fexpo.dev\u002Fblog\u002Ffrom-web-to-native-with-react",[79],"nofollow",[81],{"type":50,"value":82},"From Web to Native with React",{"type":50,"value":84}," — read that for the why.",{"type":44,"tag":86,"props":87,"children":92},"pre",{"className":88,"code":89,"language":90,"meta":91,"style":91},"language-mermaid shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","flowchart TD\n    A1[1 · Assess: write the worklist] --> A2[2 · Scaffold Expo shell]\n    A2 --> A3[3 · DOM-component shell\u003Cbr\u002F>· expo-dom · SHIP DAY ONE]\n    A3 --> A4[4 · Strangle screens to native\u003Cbr\u002F>highest-value first · expo-router]\n    A4 -->|more screens| A4\n    A4 --> A5[5 · Wire data \u002F auth \u002F storage\u003Cbr\u002F>· expo-data-fetching]\n    A5 --> A6[6 · Ship · eas-app-stores]\n","mermaid","",[93],{"type":44,"tag":94,"props":95,"children":96},"code",{"__ignoreMap":91},[97,108,117,126,135,144,153],{"type":44,"tag":98,"props":99,"children":102},"span",{"class":100,"line":101},"line",1,[103],{"type":44,"tag":98,"props":104,"children":105},{},[106],{"type":50,"value":107},"flowchart TD\n",{"type":44,"tag":98,"props":109,"children":111},{"class":100,"line":110},2,[112],{"type":44,"tag":98,"props":113,"children":114},{},[115],{"type":50,"value":116},"    A1[1 · Assess: write the worklist] --> A2[2 · Scaffold Expo shell]\n",{"type":44,"tag":98,"props":118,"children":120},{"class":100,"line":119},3,[121],{"type":44,"tag":98,"props":122,"children":123},{},[124],{"type":50,"value":125},"    A2 --> A3[3 · DOM-component shell\u003Cbr\u002F>· expo-dom · SHIP DAY ONE]\n",{"type":44,"tag":98,"props":127,"children":129},{"class":100,"line":128},4,[130],{"type":44,"tag":98,"props":131,"children":132},{},[133],{"type":50,"value":134},"    A3 --> A4[4 · Strangle screens to native\u003Cbr\u002F>highest-value first · expo-router]\n",{"type":44,"tag":98,"props":136,"children":138},{"class":100,"line":137},5,[139],{"type":44,"tag":98,"props":140,"children":141},{},[142],{"type":50,"value":143},"    A4 -->|more screens| A4\n",{"type":44,"tag":98,"props":145,"children":147},{"class":100,"line":146},6,[148],{"type":44,"tag":98,"props":149,"children":150},{},[151],{"type":50,"value":152},"    A4 --> A5[5 · Wire data \u002F auth \u002F storage\u003Cbr\u002F>· expo-data-fetching]\n",{"type":44,"tag":98,"props":154,"children":156},{"class":100,"line":155},7,[157],{"type":44,"tag":98,"props":158,"children":159},{},[160],{"type":50,"value":161},"    A5 --> A6[6 · Ship · eas-app-stores]\n",{"type":44,"tag":163,"props":164,"children":166},"h2",{"id":165},"principles",[167],{"type":50,"value":168},"Principles",{"type":44,"tag":170,"props":171,"children":172},"ul",{},[173,184,194,204,262,279],{"type":44,"tag":174,"props":175,"children":176},"li",{},[177,182],{"type":44,"tag":67,"props":178,"children":179},{},[180],{"type":50,"value":181},"Migrate, don't rewrite.",{"type":50,"value":183}," Never big-bang it; every step keeps the app shippable.",{"type":44,"tag":174,"props":185,"children":186},{},[187,192],{"type":44,"tag":67,"props":188,"children":189},{},[190],{"type":50,"value":191},"Ship on day one.",{"type":50,"value":193}," The web UI runs in a DOM-component shell (step 3) before anything is nativized — that's the milestone; everything after is polish.",{"type":44,"tag":174,"props":195,"children":196},{},[197,202],{"type":44,"tag":67,"props":198,"children":199},{},[200],{"type":50,"value":201},"Strangle by value.",{"type":50,"value":203}," Nativize the hot screens; leave the rest in the webview. Each DOM screen carries a ~2 MB web runtime — reason enough not to ship everything as DOM.",{"type":44,"tag":174,"props":205,"children":206},{},[207,212,214,227,229,234,236,242,244,249,251,260],{"type":44,"tag":67,"props":208,"children":209},{},[210],{"type":50,"value":211},"Nativize means redesign, not reskin.",{"type":50,"value":213}," A strangled screen should look like Apple\u002FGoogle shipped it, not the web page reskinned. ",{"type":44,"tag":67,"props":215,"children":216},{},[217,219,225],{"type":50,"value":218},"Reach for ",{"type":44,"tag":94,"props":220,"children":222},{"className":221},[],[223],{"type":50,"value":224},"@expo\u002Fui",{"type":50,"value":226}," first",{"type":50,"value":228}," - it renders real SwiftUI\u002FCompose, so it feels ",{"type":44,"tag":59,"props":230,"children":231},{},[232],{"type":50,"value":233},"exactly",{"type":50,"value":235}," like the OS; styled RN primitives are the fallback for custom layouts only. Plus platform navigation (",{"type":44,"tag":94,"props":237,"children":239},{"className":238},[],[240],{"type":50,"value":241},"expo-router",{"type":50,"value":243},": NativeTabs, large titles), liquid glass and native components via ",{"type":44,"tag":94,"props":245,"children":247},{"className":246},[],[248],{"type":50,"value":224},{"type":50,"value":250},", and mobile UX (sheets, swipe, haptics). The web→native pattern map is ",{"type":44,"tag":75,"props":252,"children":254},{"href":253},".\u002Freferences\u002Fnative-patterns.md",[255],{"type":44,"tag":94,"props":256,"children":258},{"className":257},[],[259],{"type":50,"value":253},{"type":50,"value":261},". If it still feels like a website, you ported instead of redesigned.",{"type":44,"tag":174,"props":263,"children":264},{},[265,270,272,277],{"type":44,"tag":67,"props":266,"children":267},{},[268],{"type":50,"value":269},"Verify by running, not compiling.",{"type":50,"value":271}," A clean build proves nothing (a blank webview compiles fine). Run each screen — but judge ",{"type":44,"tag":59,"props":273,"children":274},{},[275],{"type":50,"value":276},"content and behavior",{"type":50,"value":278}," against the web original, not pixels (a nativized screen should look more native, not identical).",{"type":44,"tag":174,"props":280,"children":281},{},[282,287,289,294,296,301,303,312],{"type":44,"tag":67,"props":283,"children":284},{},[285],{"type":50,"value":286},"Orchestrate, don't reinvent.",{"type":50,"value":288}," Each step routes into an existing skill. The value here is the ",{"type":44,"tag":59,"props":290,"children":291},{},[292],{"type":50,"value":293},"order",{"type":50,"value":295}," and the ",{"type":44,"tag":59,"props":297,"children":298},{},[299],{"type":50,"value":300},"gotchas",{"type":50,"value":302}," — the idiom-by-idiom mappings live in ",{"type":44,"tag":75,"props":304,"children":306},{"href":305},".\u002Freferences\u002Ffalse-friends.md",[307],{"type":44,"tag":94,"props":308,"children":310},{"className":309},[],[311],{"type":50,"value":305},{"type":50,"value":313},".",{"type":44,"tag":163,"props":315,"children":317},{"id":316},"run-it-as-a-loop-recommended",[318],{"type":50,"value":319},"Run it as a loop (recommended)",{"type":44,"tag":53,"props":321,"children":322},{},[323,325,330,332,341,343,348,350,356,358,363,365,371],{"type":50,"value":324},"The migration is a long repeat-until-done loop, so the first move is to ",{"type":44,"tag":67,"props":326,"children":327},{},[328],{"type":50,"value":329},"write the goal objective and launch it",{"type":50,"value":331}," — not to grind screens by hand. Fill the objective in ",{"type":44,"tag":75,"props":333,"children":335},{"href":334},".\u002Freferences\u002Frun-as-goal.md",[336],{"type":44,"tag":94,"props":337,"children":339},{"className":338},[],[340],{"type":50,"value":334},{"type":50,"value":342}," for this app and present it; it ",{"type":44,"tag":67,"props":344,"children":345},{},[346],{"type":50,"value":347},"re-reads this skill every iteration",{"type":50,"value":349},", so each ",{"type":44,"tag":94,"props":351,"children":353},{"className":352},[],[354],{"type":50,"value":355},"\u002Fgoal",{"type":50,"value":357}," turn reloads the playbook + worklist and drives the next screen (it even self-bootstraps the assess step). Then run ",{"type":44,"tag":94,"props":359,"children":361},{"className":360},[],[362],{"type":50,"value":355},{"type":50,"value":364}," with it — or, if the harness can't loop, write it to ",{"type":44,"tag":94,"props":366,"children":368},{"className":367},[],[369],{"type":50,"value":370},"migration-goal.md",{"type":50,"value":372}," and have the user launch it. The steps below are what each iteration does; run them by hand only if you're not looping.",{"type":44,"tag":163,"props":374,"children":376},{"id":375},"the-migration",[377],{"type":50,"value":378},"The migration",{"type":44,"tag":380,"props":381,"children":382},"blockquote",{},[383],{"type":44,"tag":53,"props":384,"children":385},{},[386,391,393,398,400,408],{"type":44,"tag":67,"props":387,"children":388},{},[389],{"type":50,"value":390},"No repo to migrate",{"type":50,"value":392}," - just building native fresh as a web dev? You don't need these steps: use ",{"type":44,"tag":94,"props":394,"children":396},{"className":395},[],[397],{"type":50,"value":241},{"type":50,"value":399},", and keep ",{"type":44,"tag":75,"props":401,"children":402},{"href":305},[403],{"type":44,"tag":94,"props":404,"children":406},{"className":405},[],[407],{"type":50,"value":305},{"type":50,"value":409}," open for the web→native idiom map. Everything below assumes an existing web app.",{"type":44,"tag":411,"props":412,"children":414},"h3",{"id":413},"_1-assess-write-the-worklist",[415],{"type":50,"value":416},"1. Assess → write the worklist",{"type":44,"tag":53,"props":418,"children":419},{},[420,422,428],{"type":50,"value":421},"Read the repo and produce ",{"type":44,"tag":94,"props":423,"children":425},{"className":424},[],[426],{"type":50,"value":427},"migration-progress.md",{"type":50,"value":429},", the durable worklist the rest of the migration checks off. Make two cuts:",{"type":44,"tag":170,"props":431,"children":432},{},[433,467],{"type":44,"tag":174,"props":434,"children":435},{},[436,441,443,449,451,457,459,465],{"type":44,"tag":67,"props":437,"children":438},{},[439],{"type":50,"value":440},"Screens vs backend.",{"type":50,"value":442}," Page routes (",{"type":44,"tag":94,"props":444,"children":446},{"className":445},[],[447],{"type":50,"value":448},"page.tsx",{"type":50,"value":450},") are screens you migrate; server routes (",{"type":44,"tag":94,"props":452,"children":454},{"className":453},[],[455],{"type":50,"value":456},"route.ts",{"type":50,"value":458},"), the ORM, and auth handlers stay server-side. Decide the backend once: keep it deployed (the native app becomes an HTTP client) or move it to EAS Hosting (",{"type":44,"tag":94,"props":460,"children":462},{"className":461},[],[463],{"type":50,"value":464},"eas-hosting",{"type":50,"value":466},").",{"type":44,"tag":174,"props":468,"children":469},{},[470,475,477,482,484,489,491,496,498,503],{"type":44,"tag":67,"props":471,"children":472},{},[473],{"type":50,"value":474},"Bucket each screen",{"type":50,"value":476}," by how it should land: ",{"type":44,"tag":67,"props":478,"children":479},{},[480],{"type":50,"value":481},"port-as-is",{"type":50,"value":483}," (presentational → ships in a DOM webview), ",{"type":44,"tag":67,"props":485,"children":486},{},[487],{"type":50,"value":488},"nativize-now",{"type":50,"value":490}," (hot, or needs native feel — gestures, lists, keyboard), ",{"type":44,"tag":67,"props":492,"children":493},{},[494],{"type":50,"value":495},"nativize-later",{"type":50,"value":497},", or ",{"type":44,"tag":67,"props":499,"children":500},{},[501],{"type":50,"value":502},"hybrid",{"type":50,"value":504}," (a native shell around a web sub-tree, e.g. a chat list wrapping a markdown renderer).",{"type":44,"tag":53,"props":506,"children":507},{},[508,510,515,517,523,525,530,532,537],{"type":50,"value":509},"Note the framework signals as you read — RSC vs client, Tailwind\u002Fshadcn, where data is fetched — since they decide how each screen ports (false-friends has the mappings; async Server Components in particular must be split into a client fetch + a presentational component before they can move). ",{"type":44,"tag":67,"props":511,"children":512},{},[513],{"type":50,"value":514},"Flag third-party services\u002FSDKs too",{"type":50,"value":516}," — browser SDKs don't carry over (",{"type":44,"tag":94,"props":518,"children":520},{"className":519},[],[521],{"type":50,"value":522},"false-friends",{"type":50,"value":524}," → ",{"type":44,"tag":59,"props":526,"children":527},{},[528],{"type":50,"value":529},"Services & SDKs",{"type":50,"value":531},"); payments especially is a ",{"type":44,"tag":59,"props":533,"children":534},{},[535],{"type":50,"value":536},"fork, not a swap",{"type":50,"value":538}," (in-app digital goods must use store IAP via RevenueCat, ~30% — not Stripe), a business-model call to make now, not at App Store review. The worklist is only trustworthy once every route is sorted and every screen bucketed.",{"type":44,"tag":411,"props":540,"children":542},{"id":541},"_2-scaffold-the-shell",[543],{"type":50,"value":544},"2. Scaffold the shell",{"type":44,"tag":53,"props":546,"children":547},{},[548,554,556,562,563,569,571,577],{"type":44,"tag":94,"props":549,"children":551},{"className":550},[],[552],{"type":50,"value":553},"create-expo-app",{"type":50,"value":555},", then mirror the web routes in Expo Router — Next's tree maps almost 1:1 (note ",{"type":44,"tag":94,"props":557,"children":559},{"className":558},[],[560],{"type":50,"value":561},"[id]\u002Fpage.tsx",{"type":50,"value":524},{"type":44,"tag":94,"props":564,"children":566},{"className":565},[],[567],{"type":50,"value":568},"[id].tsx",{"type":50,"value":570},", and routes may live in ",{"type":44,"tag":94,"props":572,"children":574},{"className":573},[],[575],{"type":50,"value":576},"src\u002Fapp\u002F",{"type":50,"value":578},"). Empty screens, one per route.",{"type":44,"tag":411,"props":580,"children":582},{"id":581},"_3-shell-it-in-dom-components-the-day-one-milestone",[583],{"type":50,"value":584},"3. Shell it in DOM components — the day-one milestone",{"type":44,"tag":53,"props":586,"children":587},{},[588,590,596,598,604,606,612],{"type":50,"value":589},"Bring every screen over as a DOM component (",{"type":44,"tag":94,"props":591,"children":593},{"className":592},[],[594],{"type":50,"value":595},"'use dom'",{"type":50,"value":597},", per the ",{"type":44,"tag":94,"props":599,"children":601},{"className":600},[],[602],{"type":50,"value":603},"expo-dom",{"type":50,"value":605}," skill) rendered by its native route, so the whole app runs on a phone before anything is nativized. Expect per-screen edits - unwrapping Server Components, swapping framework imports (",{"type":44,"tag":94,"props":607,"children":609},{"className":608},[],[610],{"type":50,"value":611},"next\u002Flink",{"type":50,"value":613},"), carrying the styling over - all covered in false-friends. Then verify by running (below); this is shippable to TestFlight as-is.",{"type":44,"tag":411,"props":615,"children":617},{"id":616},"_4-strangle-screens-to-native-by-value",[618],{"type":50,"value":619},"4. Strangle screens to native — by value",{"type":44,"tag":53,"props":621,"children":622},{},[623,625,630,632,637,639,648,650,658,660,665,667,675,677,682,684,689,691,697,699,704,706,710,712,720],{"type":50,"value":624},"Walk ",{"type":44,"tag":94,"props":626,"children":628},{"className":627},[],[629],{"type":50,"value":427},{"type":50,"value":631}," top-down. For each screen, ",{"type":44,"tag":59,"props":633,"children":634},{},[635],{"type":50,"value":636},"redesign",{"type":50,"value":638}," it native - don't port the web layout. Reach for ",{"type":44,"tag":67,"props":640,"children":641},{},[642,647],{"type":44,"tag":94,"props":643,"children":645},{"className":644},[],[646],{"type":50,"value":224},{"type":50,"value":226},{"type":50,"value":649}," (real SwiftUI\u002FCompose - buttons, lists, sheets, pickers, sliders; ",{"type":44,"tag":75,"props":651,"children":652},{"href":253},[653],{"type":44,"tag":94,"props":654,"children":656},{"className":655},[],[657],{"type":50,"value":253},{"type":50,"value":659}," maps which web pattern becomes which native component), then platform navigation (",{"type":44,"tag":94,"props":661,"children":663},{"className":662},[],[664],{"type":50,"value":241},{"type":50,"value":666}," - NativeTabs, large titles) and mobile UX (swipe, haptics, momentum\u002Finverted scroll); RN primitives only for custom layouts. Consult ",{"type":44,"tag":75,"props":668,"children":669},{"href":305},[670],{"type":44,"tag":94,"props":671,"children":673},{"className":672},[],[674],{"type":50,"value":305},{"type":50,"value":676}," for each idiom. ",{"type":44,"tag":94,"props":678,"children":680},{"className":679},[],[681],{"type":50,"value":224},{"type":50,"value":683}," and DOM components both run in ",{"type":44,"tag":67,"props":685,"children":686},{},[687],{"type":50,"value":688},"Expo Go",{"type":50,"value":690}," (SDK 56+) - a dev build (the ",{"type":44,"tag":94,"props":692,"children":694},{"className":693},[],[695],{"type":50,"value":696},"expo-dev-client",{"type":50,"value":698}," skill) is only needed for ",{"type":44,"tag":59,"props":700,"children":701},{},[702],{"type":50,"value":703},"custom",{"type":50,"value":705}," native modules. Verify ",{"type":44,"tag":59,"props":707,"children":708},{},[709],{"type":50,"value":276},{"type":50,"value":711}," against the running web original (the look should become more native), then check it off. One screen per pass, app shippable throughout. It's a loop over a durable worklist, so it can run unattended - hand it to a goal loop (",{"type":44,"tag":75,"props":713,"children":714},{"href":334},[715],{"type":44,"tag":94,"props":716,"children":718},{"className":717},[],[719],{"type":50,"value":334},{"type":50,"value":466},{"type":44,"tag":411,"props":722,"children":724},{"id":723},"_5-wire-data-auth-and-storage",[725],{"type":50,"value":726},"5. Wire data, auth, and storage",{"type":44,"tag":53,"props":728,"children":729},{},[730,732,738,740,746,748,753],{"type":50,"value":731},"The web data layer doesn't survive the move - relative fetches, cookie sessions, ",{"type":44,"tag":94,"props":733,"children":735},{"className":734},[],[736],{"type":50,"value":737},"localStorage",{"type":50,"value":739},", and env vars all change (swaps in false-friends). Use ",{"type":44,"tag":94,"props":741,"children":743},{"className":742},[],[744],{"type":50,"value":745},"expo-data-fetching",{"type":50,"value":747}," for requests and caching; add ",{"type":44,"tag":94,"props":749,"children":751},{"className":750},[],[752],{"type":50,"value":464},{"type":50,"value":754}," if the backend moved to EAS Hosting.",{"type":44,"tag":411,"props":756,"children":758},{"id":757},"_6-ship",[759],{"type":50,"value":760},"6. Ship",{"type":44,"tag":53,"props":762,"children":763},{},[764,770],{"type":44,"tag":94,"props":765,"children":767},{"className":766},[],[768],{"type":50,"value":769},"eas-app-stores",{"type":50,"value":771}," for the store builds (App Store \u002F Play \u002F TestFlight), EAS Update for OTA pushes after.",{"type":44,"tag":163,"props":773,"children":775},{"id":774},"verify-by-running-not-compiling",[776],{"type":50,"value":777},"Verify by running, not compiling",{"type":44,"tag":53,"props":779,"children":780},{},[781,783,789,791,796,798,803,805,810],{"type":50,"value":782},"A green ",{"type":44,"tag":94,"props":784,"children":786},{"className":785},[],[787],{"type":50,"value":788},"expo export",{"type":50,"value":790}," proves a screen ",{"type":44,"tag":59,"props":792,"children":793},{},[794],{"type":50,"value":795},"bundles",{"type":50,"value":797},", not that it ",{"type":44,"tag":59,"props":799,"children":800},{},[801],{"type":50,"value":802},"renders",{"type":50,"value":804}," — a screen can build and still render blank or mis-render. So after the shell and after every nativized screen, compare the two ",{"type":44,"tag":67,"props":806,"children":807},{},[808],{"type":50,"value":809},"running",{"type":50,"value":811}," apps for the same route:",{"type":44,"tag":170,"props":813,"children":814},{},[815,859],{"type":44,"tag":174,"props":816,"children":817},{},[818,823,825,834,836,842,844,850,852,858],{"type":44,"tag":67,"props":819,"children":820},{},[821],{"type":50,"value":822},"Web original",{"type":50,"value":824}," — capture it with ",{"type":44,"tag":67,"props":826,"children":827},{},[828],{"type":44,"tag":94,"props":829,"children":831},{"className":830},[],[832],{"type":50,"value":833},"agent-browser",{"type":50,"value":835}," (vercel-labs CLI): ",{"type":44,"tag":94,"props":837,"children":839},{"className":838},[],[840],{"type":50,"value":841},"open",{"type":50,"value":843}," the route, ",{"type":44,"tag":94,"props":845,"children":847},{"className":846},[],[848],{"type":50,"value":849},"snapshot --json",{"type":50,"value":851}," the accessibility tree, ",{"type":44,"tag":94,"props":853,"children":855},{"className":854},[],[856],{"type":50,"value":857},"screenshot",{"type":50,"value":313},{"type":44,"tag":174,"props":860,"children":861},{},[862,867,869,878,880,886,888,894,896,902],{"type":44,"tag":67,"props":863,"children":864},{},[865],{"type":50,"value":866},"Native",{"type":50,"value":868}," — drive the simulator with ",{"type":44,"tag":67,"props":870,"children":871},{},[872],{"type":44,"tag":94,"props":873,"children":875},{"className":874},[],[876],{"type":50,"value":877},"argent",{"type":50,"value":879},": ",{"type":44,"tag":94,"props":881,"children":883},{"className":882},[],[884],{"type":50,"value":885},"describe",{"type":50,"value":887}," \u002F ",{"type":44,"tag":94,"props":889,"children":891},{"className":890},[],[892],{"type":50,"value":893},"debugger-component-tree",{"type":50,"value":895}," for structure, ",{"type":44,"tag":94,"props":897,"children":899},{"className":898},[],[900],{"type":50,"value":901},"flow",{"type":50,"value":903}," to replay the check each pass.",{"type":44,"tag":53,"props":905,"children":906},{},[907,909,913,915,920,922,927,929,935,937,942,944,949,951,956,958,967],{"type":50,"value":908},"Pass on parity of ",{"type":44,"tag":67,"props":910,"children":911},{},[912],{"type":50,"value":276},{"type":50,"value":914}," — not pixels: a nativized screen should look ",{"type":44,"tag":59,"props":916,"children":917},{},[918],{"type":50,"value":919},"more",{"type":50,"value":921}," native than the web, never identical (the DOM-shell stage is the exception — there it ",{"type":44,"tag":59,"props":923,"children":924},{},[925],{"type":50,"value":926},"is",{"type":50,"value":928}," the web UI, so it should match). Feel is part of native and can't be screenshotted — for screens with transitions or gestures, capture a short recording, not just a still (see ",{"type":44,"tag":94,"props":930,"children":932},{"className":931},[],[933],{"type":50,"value":934},"native-patterns.md",{"type":50,"value":936}," → Feel). This loop is ",{"type":44,"tag":67,"props":938,"children":939},{},[940],{"type":50,"value":941},"opinionated about its tooling",{"type":50,"value":943},": if ",{"type":44,"tag":94,"props":945,"children":947},{"className":946},[],[948],{"type":50,"value":833},{"type":50,"value":950}," or ",{"type":44,"tag":94,"props":952,"children":954},{"className":953},[],[955],{"type":50,"value":877},{"type":50,"value":957}," isn't installed, ask the user and install it before proceeding — don't fall back to manual screenshots. Full recipe and setup in ",{"type":44,"tag":75,"props":959,"children":961},{"href":960},".\u002Freferences\u002Fverify-on-device.md",[962],{"type":44,"tag":94,"props":963,"children":965},{"className":964},[],[966],{"type":50,"value":960},{"type":50,"value":313},{"type":44,"tag":163,"props":969,"children":971},{"id":970},"references",[972],{"type":50,"value":973},"References",{"type":44,"tag":170,"props":975,"children":976},{},[977,990,1017,1030,1043],{"type":44,"tag":174,"props":978,"children":979},{},[980,988],{"type":44,"tag":75,"props":981,"children":982},{"href":305},[983],{"type":44,"tag":94,"props":984,"children":986},{"className":985},[],[987],{"type":50,"value":305},{"type":50,"value":989}," — web idiom → native equivalent + the gotcha for each. The lookup for steps 3–5, and for any web dev unlearning idioms.",{"type":44,"tag":174,"props":991,"children":992},{},[993,1001,1003,1008,1010,1015],{"type":44,"tag":75,"props":994,"children":995},{"href":253},[996],{"type":44,"tag":94,"props":997,"children":999},{"className":998},[],[1000],{"type":50,"value":253},{"type":50,"value":1002}," — web UX ",{"type":44,"tag":59,"props":1004,"children":1005},{},[1006],{"type":50,"value":1007},"pattern",{"type":50,"value":1009}," → native redesign (",{"type":44,"tag":94,"props":1011,"children":1013},{"className":1012},[],[1014],{"type":50,"value":224},{"type":50,"value":1016},"-first). The step-4 redesign playbook so screens feel OS-native, not reskinned.",{"type":44,"tag":174,"props":1018,"children":1019},{},[1020,1028],{"type":44,"tag":75,"props":1021,"children":1022},{"href":960},[1023],{"type":44,"tag":94,"props":1024,"children":1026},{"className":1025},[],[1027],{"type":50,"value":960},{"type":50,"value":1029}," — the two-agent parity recipe: drive the web app (browser agent) and the native app (argent), open the same route, compare.",{"type":44,"tag":174,"props":1031,"children":1032},{},[1033,1041],{"type":44,"tag":75,"props":1034,"children":1035},{"href":334},[1036],{"type":44,"tag":94,"props":1037,"children":1039},{"className":1038},[],[1040],{"type":50,"value":334},{"type":50,"value":1042}," — a ready-shaped, migration-specific goal objective for driving step 4 unattended (re-reads this skill each iteration).",{"type":44,"tag":174,"props":1044,"children":1045},{},[1046,1052],{"type":44,"tag":75,"props":1047,"children":1049},{"href":77,"rel":1048},[79],[1050],{"type":50,"value":1051},"Expo — From Web to Native with React",{"type":50,"value":1053}," — the canonical guide this skill operationalizes.",{"type":44,"tag":163,"props":1055,"children":1057},{"id":1056},"submitting-feedback",[1058],{"type":50,"value":1059},"Submitting Feedback",{"type":44,"tag":53,"props":1061,"children":1062},{},[1063],{"type":50,"value":1064},"If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:",{"type":44,"tag":86,"props":1066,"children":1070},{"className":1067,"code":1068,"language":1069,"meta":91,"style":91},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npx --yes submit-expo-feedback@latest --category skills --subject \"expo-web-to-native\" \"\u003Cactionable feedback>\"\n","bash",[1071],{"type":44,"tag":94,"props":1072,"children":1073},{"__ignoreMap":91},[1074],{"type":44,"tag":98,"props":1075,"children":1076},{"class":100,"line":101},[1077,1083,1089,1094,1099,1104,1109,1115,1119,1124,1128,1133],{"type":44,"tag":98,"props":1078,"children":1080},{"style":1079},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[1081],{"type":50,"value":1082},"npx",{"type":44,"tag":98,"props":1084,"children":1086},{"style":1085},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1087],{"type":50,"value":1088}," --yes",{"type":44,"tag":98,"props":1090,"children":1091},{"style":1085},[1092],{"type":50,"value":1093}," submit-expo-feedback@latest",{"type":44,"tag":98,"props":1095,"children":1096},{"style":1085},[1097],{"type":50,"value":1098}," --category",{"type":44,"tag":98,"props":1100,"children":1101},{"style":1085},[1102],{"type":50,"value":1103}," skills",{"type":44,"tag":98,"props":1105,"children":1106},{"style":1085},[1107],{"type":50,"value":1108}," --subject",{"type":44,"tag":98,"props":1110,"children":1112},{"style":1111},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[1113],{"type":50,"value":1114}," \"",{"type":44,"tag":98,"props":1116,"children":1117},{"style":1085},[1118],{"type":50,"value":4},{"type":44,"tag":98,"props":1120,"children":1121},{"style":1111},[1122],{"type":50,"value":1123},"\"",{"type":44,"tag":98,"props":1125,"children":1126},{"style":1111},[1127],{"type":50,"value":1114},{"type":44,"tag":98,"props":1129,"children":1130},{"style":1085},[1131],{"type":50,"value":1132},"\u003Cactionable feedback>",{"type":44,"tag":98,"props":1134,"children":1135},{"style":1111},[1136],{"type":50,"value":1137},"\"\n",{"type":44,"tag":53,"props":1139,"children":1140},{},[1141],{"type":50,"value":1142},"Only submit when you have something specific and actionable to report. Include as much relevant context as possible.",{"type":44,"tag":1144,"props":1145,"children":1146},"style",{},[1147],{"type":50,"value":1148},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1150,"total":1317},[1151,1168,1182,1195,1209,1223,1241,1252,1267,1282,1296,1306],{"slug":769,"name":769,"fn":1152,"description":1153,"org":1154,"tags":1155,"stars":26,"repoUrl":27,"updatedAt":1167},"deploy and submit Expo apps to stores","EAS service (paid). Deploy Expo apps to the app stores with EAS - build and submit to the iOS App Store, Google Play Store, and TestFlight, configure eas.json build and submit profiles, manage app versions and build numbers, and publish App Store metadata and ASO. Use whenever the user wants to deploy, release, or ship an app to production or the app stores, is preparing a production build, running eas build or eas submit, shipping to TestFlight, bumping version or build numbers, or setting up store listing metadata. For deploying an Expo website or API routes, use the eas-hosting skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1156,1159,1162,1163,1166],{"name":1157,"slug":1158,"type":15},"Android","android",{"name":1160,"slug":1161,"type":15},"Deployment","deployment",{"name":9,"slug":8,"type":15},{"name":1164,"slug":1165,"type":15},"iOS","ios",{"name":24,"slug":25,"type":15},"2026-07-24T05:36:44.164663",{"slug":464,"name":464,"fn":1169,"description":1170,"org":1171,"tags":1172,"stars":26,"repoUrl":27,"updatedAt":1181},"deploy Expo websites to EAS Hosting","EAS service (paid). Deploy Expo websites and Expo Router API routes to EAS Hosting - export the web bundle, run eas deploy for production and PR preview URLs, manage environment secrets and custom domains, and work within the Cloudflare Workers runtime. Also covers authoring API routes (+api.ts handlers, HTTP methods, request handling, CORS). Use when deploying an Expo web app or API routes, setting up EAS Hosting, or configuring hosting environments and domains. Not for native builds or store releases - use the eas-app-stores skill for those.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1173,1174,1175,1178],{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":1176,"slug":1177,"type":15},"Frontend","frontend",{"name":1179,"slug":1180,"type":15},"Web Development","web-development","2026-07-24T05:36:40.193701",{"slug":1183,"name":1183,"fn":1184,"description":1185,"org":1186,"tags":1187,"stars":26,"repoUrl":27,"updatedAt":1194},"eas-observe","configure EAS Observe for Expo apps","EAS service (paid). Use for anything related to EAS Observe - adding `expo-observe` to an Expo project (AppMetricsRoot\u002FObserveRoot HOC, markInteractive, the useObserve hook, the Expo Router \u002F React Navigation integrations for per-route metrics, and user-defined events via `Observe.logEvent`), querying via the EAS CLI (`eas observe:metrics-summary`, `observe:metrics`, `observe:routes`, `observe:events`, `observe:versions`), or interpreting the resulting metrics (cold\u002Fwarm launch, TTR, TTI, navigation cold\u002Fwarm TTR, update download, and the TTI frameRate params for triaging slow startups).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1188,1189,1190,1193],{"name":9,"slug":8,"type":15},{"name":24,"slug":25,"type":15},{"name":1191,"slug":1192,"type":15},"Observability","observability",{"name":17,"slug":18,"type":15},"2026-07-24T05:36:45.220605",{"slug":1196,"name":1196,"fn":1197,"description":1198,"org":1199,"tags":1200,"stars":26,"repoUrl":27,"updatedAt":1208},"eas-simulator","run and control remote iOS and Android simulators","EAS service (paid). Run and control a user's app on a remote iOS\u002FAndroid simulator hosted on EAS cloud. Read before running any `eas simulator:*` commands - it has the current syntax for this experimental API. Use whenever the user needs a simulator they can't run locally - 'run my app on a cloud simulator', 'use eas simulator to run\u002Finstall\u002Fscreenshot my app', 'I'm on Linux\u002FCursor and need an iOS device', 'no sim on this box \u002F headless CI', 'let an agent click through my app and screenshot it', 'test my dev build on a remote sim with live reload', 'stream a sim to my browser' - even when they don't say 'EAS Simulator' or 'cloud'. On a host WITHOUT a local simulator (Linux, CI, cloud sandbox) it's the default; on macOS, do NOT auto-trigger for a plain 'run on the simulator' - use it only for a cloud\u002Fremote\u002Fshareable sim, an iOS version they lack, or an agent-driven session. NOT for local sims (expo run:ios, Xcode, Android Studio), EAS Build\u002FUpdate, web preview, or physical devices.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1201,1202,1205,1206,1207],{"name":1157,"slug":1158,"type":15},{"name":1203,"slug":1204,"type":15},"CLI","cli",{"name":9,"slug":8,"type":15},{"name":1164,"slug":1165,"type":15},{"name":24,"slug":25,"type":15},"2026-07-31T05:52:18.602058",{"slug":1210,"name":1210,"fn":1211,"description":1212,"org":1213,"tags":1214,"stars":26,"repoUrl":27,"updatedAt":1222},"eas-update-insights","check health of EAS Updates","EAS service (paid). Check the health of published EAS Update: crash rates, install\u002Flaunch counts, unique users, payload size, and the split between embedded and OTA users per channel. Use when the user asks how an update is performing, whether a rollout is healthy, how many users are on the embedded build vs OTA, or wants to gate CI on update health.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1215,1218,1219,1220,1221],{"name":1216,"slug":1217,"type":15},"Analytics","analytics",{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":24,"slug":25,"type":15},{"name":1191,"slug":1192,"type":15},"2026-07-24T05:36:50.17095",{"slug":1224,"name":1224,"fn":1225,"description":1226,"org":1227,"tags":1228,"stars":26,"repoUrl":27,"updatedAt":1240},"eas-workflows","configure EAS workflows for Expo projects","EAS service (paid). Helps understand and write EAS workflow YAML files for Expo projects. Use this skill when the user asks about CI\u002FCD or workflows in an Expo or EAS context, mentions .eas\u002Fworkflows\u002F, or wants help with EAS build pipelines or deployment automation.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1229,1232,1235,1236,1237],{"name":1230,"slug":1231,"type":15},"Automation","automation",{"name":1233,"slug":1234,"type":15},"CI\u002FCD","ci-cd",{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":1238,"slug":1239,"type":15},"YAML","yaml","2026-07-24T05:36:43.205514",{"slug":1242,"name":1242,"fn":1243,"description":1244,"org":1245,"tags":1246,"stars":26,"repoUrl":27,"updatedAt":1251},"expo-app-clip","add iOS App Clip targets to Expo","Framework (OSS). Add an iOS App Clip target to an Expo app. Use when the user mentions App Clip, AASA, apple-app-site-association, appclips, smart app banner, or wants to ship a lightweight iOS Clip invoked from a URL alongside their parent app.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1247,1248,1249,1250],{"name":9,"slug":8,"type":15},{"name":1164,"slug":1165,"type":15},{"name":24,"slug":25,"type":15},{"name":17,"slug":18,"type":15},"2026-07-24T05:36:46.195935",{"slug":1253,"name":1253,"fn":1254,"description":1255,"org":1256,"tags":1257,"stars":26,"repoUrl":27,"updatedAt":1266},"expo-brownfield","integrate Expo and React Native into native apps","Framework (OSS). Integrate Expo and React Native into an existing native iOS or Android app. Use when the user mentions brownfield, embedding React Native in a native app, AAR\u002FXCFramework, or adding Expo to an existing Kotlin\u002FSwift project. Covers both the isolated approach and the integrated approach.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1258,1259,1260,1263,1264,1265],{"name":1157,"slug":1158,"type":15},{"name":9,"slug":8,"type":15},{"name":1261,"slug":1262,"type":15},"Integrations","integrations",{"name":1164,"slug":1165,"type":15},{"name":24,"slug":25,"type":15},{"name":17,"slug":18,"type":15},"2026-07-24T05:36:39.682299",{"slug":745,"name":745,"fn":1268,"description":1269,"org":1270,"tags":1271,"stars":26,"repoUrl":27,"updatedAt":1281},"fetch and manage data in Expo apps","Framework (OSS). Use when implementing or debugging ANY network request, API call, or data fetching. Covers fetch API, React Query, SWR, error handling, caching, offline support, and Expo Router data loaders (`useLoaderData`).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1272,1275,1278,1279,1280],{"name":1273,"slug":1274,"type":15},"API Development","api-development",{"name":1276,"slug":1277,"type":15},"Caching","caching",{"name":9,"slug":8,"type":15},{"name":24,"slug":25,"type":15},{"name":20,"slug":21,"type":15},"2026-07-24T05:36:42.177188",{"slug":696,"name":696,"fn":1283,"description":1284,"org":1285,"tags":1286,"stars":26,"repoUrl":27,"updatedAt":1295},"build and distribute Expo development clients","Framework (OSS). Build and distribute Expo development clients locally or via TestFlight for internal testing. For production TestFlight releases and store submission, use the eas-app-stores skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1287,1288,1289,1290,1291,1294],{"name":1157,"slug":1158,"type":15},{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":1164,"slug":1165,"type":15},{"name":1292,"slug":1293,"type":15},"Local Development","local-development",{"name":24,"slug":25,"type":15},"2026-07-24T05:36:51.160719",{"slug":603,"name":603,"fn":1297,"description":1298,"org":1299,"tags":1300,"stars":26,"repoUrl":27,"updatedAt":1305},"run web components in native apps","Framework (OSS). Use Expo DOM components to run web code in a webview on native and as-is on web. Migrate web code to native incrementally. For the end-to-end migration of a whole web app, use the expo-web-to-native skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1301,1302,1303,1304],{"name":9,"slug":8,"type":15},{"name":1176,"slug":1177,"type":15},{"name":24,"slug":25,"type":15},{"name":17,"slug":18,"type":15},"2026-07-24T05:36:41.176872",{"slug":1307,"name":1307,"fn":1308,"description":1309,"org":1310,"tags":1311,"stars":26,"repoUrl":27,"updatedAt":1316},"expo-examples","integrate Expo example projects","Framework (OSS). Expo's official example projects - the expo\u002Fexamples repo of ~70 `with-*` integrations (Stripe, Clerk, Supabase, OpenAI, maps, Reanimated, SQLite, Skia, NativeWind, and more). Use when integrating a third-party library or service into an existing Expo app and you want the canonical, version-matched pattern to adapt, or when scaffolding a new project from one with `npx create-expo --example`.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1312,1313,1314,1315],{"name":9,"slug":8,"type":15},{"name":1261,"slug":1262,"type":15},{"name":24,"slug":25,"type":15},{"name":17,"slug":18,"type":15},"2026-07-24T05:36:35.174379",24,{"items":1319,"total":1373},[1320,1328,1335,1342,1350,1358,1366],{"slug":769,"name":769,"fn":1152,"description":1153,"org":1321,"tags":1322,"stars":26,"repoUrl":27,"updatedAt":1167},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1323,1324,1325,1326,1327],{"name":1157,"slug":1158,"type":15},{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":1164,"slug":1165,"type":15},{"name":24,"slug":25,"type":15},{"slug":464,"name":464,"fn":1169,"description":1170,"org":1329,"tags":1330,"stars":26,"repoUrl":27,"updatedAt":1181},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1331,1332,1333,1334],{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":1176,"slug":1177,"type":15},{"name":1179,"slug":1180,"type":15},{"slug":1183,"name":1183,"fn":1184,"description":1185,"org":1336,"tags":1337,"stars":26,"repoUrl":27,"updatedAt":1194},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1338,1339,1340,1341],{"name":9,"slug":8,"type":15},{"name":24,"slug":25,"type":15},{"name":1191,"slug":1192,"type":15},{"name":17,"slug":18,"type":15},{"slug":1196,"name":1196,"fn":1197,"description":1198,"org":1343,"tags":1344,"stars":26,"repoUrl":27,"updatedAt":1208},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1345,1346,1347,1348,1349],{"name":1157,"slug":1158,"type":15},{"name":1203,"slug":1204,"type":15},{"name":9,"slug":8,"type":15},{"name":1164,"slug":1165,"type":15},{"name":24,"slug":25,"type":15},{"slug":1210,"name":1210,"fn":1211,"description":1212,"org":1351,"tags":1352,"stars":26,"repoUrl":27,"updatedAt":1222},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1353,1354,1355,1356,1357],{"name":1216,"slug":1217,"type":15},{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":24,"slug":25,"type":15},{"name":1191,"slug":1192,"type":15},{"slug":1224,"name":1224,"fn":1225,"description":1226,"org":1359,"tags":1360,"stars":26,"repoUrl":27,"updatedAt":1240},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1361,1362,1363,1364,1365],{"name":1230,"slug":1231,"type":15},{"name":1233,"slug":1234,"type":15},{"name":1160,"slug":1161,"type":15},{"name":9,"slug":8,"type":15},{"name":1238,"slug":1239,"type":15},{"slug":1242,"name":1242,"fn":1243,"description":1244,"org":1367,"tags":1368,"stars":26,"repoUrl":27,"updatedAt":1251},{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1369,1370,1371,1372],{"name":9,"slug":8,"type":15},{"name":1164,"slug":1165,"type":15},{"name":24,"slug":25,"type":15},{"name":17,"slug":18,"type":15},22]