web-to-native
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWeb to Native
Web 转原生应用
A 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 — read that for the why.
mermaid
flowchart TD
A1[1 · Assess: write the worklist] --> A2[2 · Scaffold Expo shell]
A2 --> A3[3 · DOM-component shell<br/>· use-dom · SHIP DAY ONE]
A3 --> A4[4 · Strangle screens to native<br/>highest-value first · building-native-ui]
A4 -->|more screens| A4
A4 --> A5[5 · Wire data / auth / storage<br/>· native-data-fetching]
A5 --> A6[6 · Ship · expo-deployment]Web React应用无法直接转换为原生应用——不存在这样的转译工具。它需要逐步迁移,逐个页面进行,就像绞杀榕缠绕树木并慢慢取代它的过程:先搭建一个原生外壳,第一天就将整个Web UI运行在其中,然后按照优先级逐个将页面重构为原生页面。本技能是指导这项工作的核心框架;每个步骤都会衔接现有的Expo技能,而非重复解释细节。它将Expo的《From Web to Native with React》指南落地为可执行的步骤——若想了解迁移的原因,请阅读该指南。
mermaid
flowchart TD
A1[1 · Assess: write the worklist] --> A2[2 · Scaffold Expo shell]
A2 --> A3[3 · DOM-component shell<br/>· use-dom · SHIP DAY ONE]
A3 --> A4[4 · Strangle screens to native<br/>highest-value first · building-native-ui]
A4 -->|more screens| A4
A4 --> A5[5 · Wire data / auth / storage<br/>· native-data-fetching]
A5 --> A6[6 · Ship · expo-deployment]Principles
迁移原则
- Migrate, don't rewrite. Never big-bang it; every step keeps the app shippable.
- 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.
- 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.
- Nativize means redesign, not reskin. A strangled screen should look like Apple/Google shipped it, not the web page reskinned. Reach for first — it renders real SwiftUI/Compose, so it feels exactly like the OS; styled RN primitives are the fallback for custom layouts only. Plus platform navigation (
@expo/ui: NativeTabs, large titles), liquid glass and native components viabuilding-native-ui, and mobile UX (sheets, swipe, haptics). The web→native pattern map is@expo/ui. If it still feels like a website, you ported instead of redesigned../references/native-patterns.md - 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).
- 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 .
./references/false-friends.md
- 迁移而非重写:绝不采用大爆炸式重构;每一步都要保证应用可发布。
- 首日即可发布:在任何原生重构工作开始前,Web UI就可以运行在DOM组件外壳中(步骤3)——这是关键里程碑;后续所有工作都是优化。
- 按价值优先级重构:优先重构高频使用的页面;其余页面保留在WebView中。每个DOM页面会占用约2MB的Web运行时——这足以成为不将所有页面都转为DOM形式的理由。
- 原生重构意味着重新设计,而非换皮:重构后的页面应具备苹果/谷歌官方应用的风格,而非Web页面的换皮版本。优先使用——它会渲染真正的SwiftUI/Compose组件,因此完全贴合系统风格;仅在需要自定义布局时才使用样式化的RN基础组件。此外还需适配平台导航(
@expo/ui:NativeTabs、大标题)、液态玻璃效果以及通过building-native-ui实现的原生组件,还有移动UX(底部弹窗、滑动操作、触觉反馈)。Web→原生的模式映射可查看@expo/ui。如果页面仍像Web网站,说明你只是移植而非重新设计。./references/native-patterns.md - 通过运行验证,而非编译:成功编译不代表任何问题(空白WebView也能正常编译)。要运行每个页面进行验证——但需对照Web原版验证内容和行为,而非像素(原生重构后的页面应更具原生风格,而非与Web版完全一致)。
- 统筹协调,而非重复造轮子:每个步骤都会关联到现有技能。本技能的价值在于提供步骤顺序和避坑指南——具体的语法映射可查看。
./references/false-friends.md
Run it as a loop (recommended)
推荐以循环方式执行
The 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 for this app and present it; it re-reads this skill every iteration, so each turn reloads the playbook + worklist and drives the next screen (it even self-bootstraps the assess step). Then run with it — or, if the harness can't loop, write it to and have the user launch it. The steps below are what each iteration does; run them by hand only if you're not looping.
./references/run-as-goal.md/goal/goalmigration-goal.md迁移是一个需要重复执行直至完成的长期过程,因此第一步是编写目标任务并启动——而非手动逐个页面处理。为此应用填写中的目标任务并启动;它会在每次迭代时重新读取本技能,因此每次执行都会重新加载指南和任务清单,推进下一个页面的迁移(甚至可以自动完成评估步骤)。之后使用该任务执行——如果无法循环执行,可将其写入并让用户启动。以下是每次迭代的具体步骤;仅当无法循环时才手动执行。
./references/run-as-goal.md/goal/goalmigration-goal.mdThe migration
迁移步骤
No repo to migrate — just building native fresh as a web dev? You don't need these steps: use, and keepbuilding-native-uiopen for the web→native idiom map. Everything below assumes an existing web app../references/false-friends.md
无现有仓库可迁移——仅作为Web开发者从零构建原生应用?你无需遵循以下步骤:使用技能,并随时查看building-native-ui中的Web→原生语法映射。以下所有步骤均假设存在现有Web应用。./references/false-friends.md
1. Assess → write the worklist
1. 评估 → 编写任务清单
Read the repo and produce , the durable worklist the rest of the migration checks off. Make two cuts:
migration-progress.md- Screens vs backend. Page routes () are screens you migrate; server routes (
page.tsx), 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 (route.ts).expo-api-routes - 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).
Note the framework signals as you read — RSC vs client, Tailwind/shadcn, 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/SDKs too — browser SDKs don't carry over ( → 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.
false-friends读取仓库内容并生成,这是后续迁移工作的持久任务清单。需完成两项分类:
migration-progress.md- 页面与后端分离:页面路由()是需要迁移的页面;服务器路由(
page.tsx)、ORM和认证处理程序保留在服务器端。一次性确定后端方案:保持现有部署(原生应用作为HTTP客户端),或迁移至EAS Hosting(使用route.ts)。expo-api-routes - 为每个页面分类:确定每个页面的处理方式:原样移植(展示型页面→在DOM WebView中发布)、立即原生重构(高频使用或需要原生体验——手势、列表、键盘交互)、后续原生重构,或混合模式(原生外壳包裹Web子树,例如聊天列表包裹Markdown渲染器)。
读取仓库时需注意框架特征——RSC与客户端组件、Tailwind/shadcn、数据获取位置——这些将决定每个页面的移植方式(中有对应映射;尤其是异步Server Components,必须拆分为客户端获取组件+展示组件后才能迁移)。同时标记第三方服务/SDK——浏览器SDK无法直接迁移(→Services & SDKs);尤其是支付功能,必须重新实现而非替换(应用内数字商品必须使用通过RevenueCat接入的应用内购买,抽成约30%——不能使用Stripe),这是需要现在就确定的业务决策,而非等到App Store审核时才处理。只有当所有路由都完成分类、所有页面都完成归类后,任务清单才是可靠的。
false-friendsfalse-friends2. Scaffold the shell
2. 搭建原生外壳
create-expo-app[id]/page.tsx[id].tsxsrc/app/使用创建项目,然后在Expo Router中镜像Web路由——Next.js的路由树几乎可以1:1映射(注意→,路由可能位于目录下)。创建空页面,每个路由对应一个页面。
create-expo-app[id]/page.tsx[id].tsxsrc/app/3. Shell it in DOM components — the day-one milestone
3. 用DOM组件包裹——首日里程碑
Bring every screen over as a DOM component (, per the 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 (), carrying the styling over — all covered in false-friends. Then verify by running (below); this is shippable to TestFlight as-is.
'use dom'use-domnext/link将所有页面作为DOM组件(按照技能的要求使用)引入,由原生路由渲染,这样在任何原生重构工作开始前,整个应用就能在手机上运行。每个页面可能需要一些修改——比如解开Server Components、替换框架导入(如)、迁移样式——这些都在中有说明。然后通过运行验证(见下文);此时应用即可发布至TestFlight。
use-dom'use dom'next/linkfalse-friends4. Strangle screens to native — by value
4. 按价值优先级将页面重构为原生
Walk top-down. For each screen, redesign it native — don't port the web layout. Reach for first (real SwiftUI/Compose — buttons, lists, sheets, pickers, sliders; maps which web pattern becomes which native component), then platform navigation ( — NativeTabs, large titles) and mobile UX (swipe, haptics, momentum/inverted scroll); RN primitives only for custom layouts. Consult for each idiom. and DOM components both run in Expo Go (SDK 56+) — a dev build (the 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 ().
migration-progress.md@expo/ui./references/native-patterns.mdbuilding-native-ui./references/false-friends.md@expo/uiexpo-dev-client./references/run-as-goal.md按照的顺序处理。对于每个页面,重新设计为原生风格——而非移植Web布局。优先使用(真正的SwiftUI/Compose组件——按钮、列表、底部弹窗、选择器、滑块;中列出了Web模式对应的原生组件),然后适配平台导航(——NativeTabs、大标题)和移动UX(滑动操作、触觉反馈、惯性/反向滚动);仅在需要自定义布局时才使用RN基础组件。每个语法点可参考。和DOM组件都能在Expo Go(SDK 56+)中运行——仅当需要自定义原生模块时才需要开发构建版本(使用技能)。对照运行中的Web原版验证内容和行为(外观应更具原生风格),然后标记为完成。每次处理一个页面,全程保证应用可发布。这是一个基于持久任务清单的循环过程,因此可以无人值守执行——将其交给目标循环()即可。
migration-progress.md@expo/ui./references/native-patterns.mdbuilding-native-ui./references/false-friends.md@expo/uiexpo-dev-client./references/run-as-goal.md5. Wire data, auth, and storage
5. 连接数据、认证与存储
The web data layer doesn't survive the move — relative fetches, cookie sessions, , and env vars all change (swaps in false-friends). Use for requests and caching; add if the backend moved to EAS Hosting.
localStoragenative-data-fetchingexpo-api-routesWeb数据层无法直接迁移——相对路径请求、Cookie会话、和环境变量都需要调整(中有对应替换方案)。使用技能处理请求和缓存;如果后端迁移至EAS Hosting,则添加。
localStoragefalse-friendsnative-data-fetchingexpo-api-routes6. Ship
6. 发布
expo-deployment使用技能构建应用商店版本(App Store / Google Play / TestFlight),之后使用EAS Update进行OTA推送。
expo-deploymentVerify by running, not compiling
通过运行验证,而非编译
A green 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:
expo export- Web original — capture it with (vercel-labs CLI):
agent-browserthe route,openthe accessibility tree,snapshot --json.screenshot - Native — drive the simulator with :
argent/describefor structure,debugger-component-treeto replay the check each pass.flow
Pass 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 → Feel). This loop is opinionated about its tooling: if or isn't installed, ask the user and install it before proceeding — don't fall back to manual screenshots. Full recipe and setup in .
native-patterns.mdagent-browserargent./references/verify-on-device.mdexpo export- Web原版——使用****(vercel-labs CLI)捕获:
agent-browser打开路由,open获取无障碍树,snapshot --json截图。screenshot - 原生版——使用****驱动模拟器:
argent/describe查看结构,debugger-component-tree在每次迭代时重复验证。flow
验证通过的标准是内容和行为一致——而非像素完全相同:原生重构后的页面应比Web版更具原生风格,绝不能完全一致(DOM外壳阶段除外——此时就是Web UI,因此应与Web版一致)。原生体验的触感无法通过截图体现——对于带有过渡动画或手势的页面,应录制短视频而非仅截图(见→Feel)。本流程对工具选型有明确要求:如果未安装或,请告知用户并先安装——不要退而求其次使用手动截图。完整的流程和设置可查看。
native-patterns.mdagent-browserargent./references/verify-on-device.mdReferences
参考资料
- — web idiom → native equivalent + the gotcha for each. The lookup for steps 3–5, and for any web dev unlearning idioms.
./references/false-friends.md - — web UX pattern → native redesign (
./references/native-patterns.md-first). The step-4 redesign playbook so screens feel OS-native, not reskinned.@expo/ui - — the two-agent parity recipe: drive the web app (browser agent) and the native app (argent), open the same route, compare.
./references/verify-on-device.md - — a ready-shaped, migration-specific goal objective for driving step 4 unattended (re-reads this skill each iteration).
./references/run-as-goal.md - Expo — From Web to Native with React — the canonical guide this skill operationalizes.
- ——Web语法→原生等效方案+每个方案的注意事项。是步骤3-5的参考手册,也是Web开发者摒弃旧语法的指南。
./references/false-friends.md - ——Web UX模式→原生重新设计(优先使用
./references/native-patterns.md)。是步骤4的重新设计指南,确保页面具备系统原生风格而非换皮。@expo/ui - ——双代理一致性验证流程:驱动Web应用(浏览器代理)和原生应用(argent),打开同一路由进行对比。
./references/verify-on-device.md - ——预定义的迁移专用目标任务,用于无人值守执行步骤4(每次迭代时重新读取本技能)。
./references/run-as-goal.md - Expo — From Web to Native with React——本技能落地的官方指南。