• Projects
  • Service
  • About
  • branding.bz
  • Podcast
  • Tips
  • FAQ
  • Recruit
  • Download
  • Contact
  • branding.bz(ブランド構築SaaS)
  • DESIGN NOW(デザインメディア)
  • X
  • LinkedIn
  • Spotify
  • Facebook

213-0011 神奈川県川崎市高津区久本3-6-7-303

© 2026 ID INC. All rights reserved

claude-skills/スキル
SKILLOfficialdevelopment

expo-web-to-native

プラグイン
expo
ライセンス
MIT
ソース
GitHub で見る ↗
説明

Framework(オープンソースソフトウェア)。Expoを使って、既存のウェブ上のReactアプリをiOS/Android用のネイティブアプリに移行します。 次のような場合に使用: ウェブサイトをモバイルアプリに変えたい、Next.js/Vite/CRAなどのReactコードベースをReact Nativeに移植したい、ウェブのコードをネイティブアプリで段階的に再利用したい、あるいはDOM、CSS、React Router、localStorage、windowといったウェブの標準的な仕組みがネイティブアプリではどのように対応するのかを知りたい場合です。 このスキルはアプリ移行の全体的なガイドを提供します。DOM(文書構造)関連の部品メカニズムについては、`expo-dom`スキルを参照してください。

原文を表示

Framework (OSS). Migrate an existing web React app to a native iOS/Android app with Expo. Use when the user wants to turn a website into a mobile app, port a Next.js/Vite/CRA 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.

ユースケース
  • ウェブサイトをモバイルアプリに変えたい
  • ReactコードベースをReact Nativeに移植したい
  • ウェブのコードをネイティブアプリで段階的に再利用したい
  • ウェブの標準的な仕組みのネイティブ対応を知りたい
本文(日本語訳)

ウェブ から ネイティブ へ

Webアプリ(React製)は、ネイティブアプリに「変換」されません — トランスパイラ(コードの自動翻訳)はありません。代わりに「移行」します。画面ごとに、絞め殺し無花果が樹を覆い尽くして置き換えていくように:まずネイティブの外枠を用意し、1日目から中身のウェブUIをその中で動かし、その後、優先度の高い順に画面をひとつずつネイティブに変えていきます。このスキルは、その作業の進め方を整理する骨組みです。各ステップでは、既存のExpoスキルに渡す形になり、説明を繰り返しません。ExpoのブログRecord「From Web to Native with React」を実装するものです — その理由についてはそちらをお読みください。

flowchart TD
    A1[1 · 現状把握: 作業リストを作成] --> A2[2 · Expoの外枠を組み立て]
    A2 --> A3[3 · DOM コンポーネント外枠<br/>· expo-dom · 初日公開]
    A3 --> A4[4 · 画面をネイティブに変更<br/>高い価値から順に · expo-router]
    A4 -->|残りの画面| A4
    A4 --> A5[5 · データ・認証・保存を接続<br/>· expo-data-fetching]
    A5 --> A6[6 · 公開 · eas-app-stores]

基本原則

  • 書き直さず、移行する。 一気に全部やり直さない。毎ステップで、アプリは公開可能な状態に保つ。
  • 初日に公開する。 DOM コンポーネント外枠(ステップ3)でウェブUIが動く状態が最初の目標です — その時点で公開。ここからはすべて細部の改善です。
  • 価値の高い画面からネイティブに変える。 ホットな画面だけネイティブ化し、残りはウェブビュー(ウェブ表示)のままでいい。各DOMの画面は約2MBのウェブランタイムを積みますから、すべてをネイティブにする理由はありません。
  • ネイティブ化は「デザイン変更」。「外見を直す」のではない。 変更後の画面は、Apple や Google が作ったように見えるべき — ウェブページに皮を被せたものではなく。@expo/uiを最初に選ぶ — 実物のSwiftUI / Composeを描画するので、OS そのものの感覚です。スタイル済みの React Native パーツは、カスタムレイアウトだけの最後の手段。加えて、プラットフォーム固有のナビゲーション(expo-router: ネイティブタブ、大きなタイトル)、グラス風エフェクト、@expo/ui経由のネイティブコンポーネント、モバイルならではのUX(シート、スワイプ、バイブ)を使う。ウェブ → ネイティブのパターン対応表は./references/native-patterns.mdです。「ウェブサイトに見える」なら、移植したのであって、デザイン変更ができていません。
  • コンパイルではなく、実行して確認する。 ビルドが成功しても何も証明されません(空のウェブビューでもコンパイルは通ります)。実際に画面を動かし、ピクセル単位ではなく「内容と動作」をウェブ版と比べて判定してください。ネイティブ化した画面は、もっとネイティブに見えるべき、完全に同じに見えるべきではありません。
  • 編成する。再発明しない。 各ステップは既存スキルに結びつきます。ここでの価値は「順序」と「落とし穴」です — 言い方や対応の細部は./references/false-friends.mdに書いてあります。

ループで回す(推奨)

移行は「完了まで何度も繰り返す」ループなので、最初は ゴール目標を書いて起動する — 画面を手作業でひとつずついじらない。./references/run-as-goal.mdに目標を入れて見せてください。ループの度にこのスキルを読み直すので、/goalを実行するたびに、遊び書 + 作業リストが更新され、次の画面へ進みます(評価ステップも自動で始まります)。それから/goalで実行するか、ループシステムが使えなければmigration-goal.mdに書いて、ユーザーに起動させてください。以下は各ループが何をするかです — ループを使わない場合は手作業でやってください。

移行の手順

移行するレポジトリがない — ウェブ開発者として新しくネイティブを作りますか? これらのステップは不要です:expo-routerを使って、./references/false-friends.mdを参考に。以下はすべて、既存のウェブアプリがあることを前提にしています。

1. 現状把握 → 作業リストを作成

レポジトリを読んで、migration-progress.mdを作ってください — 残りの移行全体で何度も見る、やること一覧です。2つに分ける:

  • 画面 vs バックエンド。 ページルート(page.tsx)は移行する画面。サーバールート(route.ts)、ORM(データベース層)、認証処理は、サーバー側に残す。バックエンドの方針を一度決める:デプロイしたままか、EAS Hosting(ホスティングサービス)に移すか。
  • 画面ごとに区分 — どう仕上げるか:そのまま移す(見た目だけ → DOMウェブビューで公開)、今すぐネイティブ化(よく使う、ネイティブの感覚が必要 — ジェスチャー、リスト、キーボード)、あとでネイティブ化、ハイブリッド(チャットリストがマークダウンレンダラーを包む、みたいにネイティブ外枠でウェブ部分を囲む)。

読みながら、フレームワークのシグナルに目を通す — RSC(サーバーコンポーネント)か クライアント、Tailwind / shadcn、データ取得の場所 — 各画面がどう移るかを決めるから。(false-friendsに対応表あり。特に非同期サーバーコンポーネントは、移行の前に「クライアント側での取得」と「表示用コンポーネント」に分ける必要があります)。外部サービス / SDK も記しておく — ブラウザSDKは持ち運べません(false-friends → サービス・SDK参照)。決済は特に「入れ替え」ではなく「分岐」(デジタル商品はアプリ内課金で、RevenueCatを介してApp Store / Play Storeの仕組みを使う、~30% — Stripe ではない)。これはアプリストア審査の直前ではなく、今決める事業判断です。ルートがすべて整理され、画面が分類され終わるまで、この作業リストは信頼できません。

2. 外枠を組み立てる

create-expo-appをしてから、ウェブのルートをExpo Routerに写す — Next.jsの構造はほぼ1対1です([id]/page.tsx → [id].tsx、ルートはsrc/app/にあるかもしれません)。空の画面をひとつずつ作ります。

3. DOMコンポーネントで囲む — 初日の目標

すべての画面をDOMコンポーネント('use dom'、expo-domスキル参照)として持ってきて、ネイティブルートが描画するようにします。アプリ全体が、何もネイティブ化される前に、スマートフォンで動く。画面ごとの手直しを覚悟してください — サーバーコンポーネントをほどく、フレームワークのインポート(next/link)を入れ替える、スタイルを運ぶ — すべてfalse-friendsに書いてあります。実行して確認(下参照)してください。この状態のまま TestFlight に公開できます。

4. 画面をネイティブに変える — 価値の高い順に

migration-progress.mdを上から順に。各画面をネイティブにデザイン変更する — ウェブレイアウトを移さない。@expo/uiを最初に — 本物のSwiftUI / Compose(ボタン、リスト、シート、ピッカー、スライダー。./references/native-patterns.mdで、ウェブパターン → ネイティブコンポーネントを対応付け)。次にプラットフォーム固有のナビゲーション(expo-router — ネイティブタブ、大きなタイトル)とモバイルUX(スワイプ、バイブ、勢いのあるスクロール)。React Nativeの基本パーツはカスタムレイアウト用だけ。各言い方について./references/false-friends.mdを見てください。@expo/uiとDOMコンポーネントは両方ともExpo Go(SDK 56以上)で動きます — dev build(expo-dev-clientスキル)はカスタム・ネイティブモジュール用だけです。内容と動作をウェブ版と比べて確認してください(見た目はもっとネイティブになるはず)。チェックして、次へ。1画面ずつ、アプリは常に公開可能。作業リストが不変なので、無人で回せます — ゴール・ループに任せて(./references/run-as-goal.md)。

5. データ・認証・保存を接続する

ウェブのデータ層は移行の時点で使えなくなります — 相対パスでの取得、クッキーセッション、localStorage、環境変数すべて変わります(入れ替え内容はfalse-friendsに)。expo-data-fetchingをリクエスト・キャッシング用に。バックエンドがEAS Hostingに移った場合はeas-hostingを足します。

6. 公開する

ストアビルド(App Store / Play / TestFlight)はeas-app-storesで。公開後のOTA配信(無線アップデート)はEAS Updateで。

実行して確認、コンパイルではなく

expo exportが成功しても、画面が描画されるとは限りません — 空白のまま、あるいは崩れて表示される場合もあります。外枠を作ったあと、ネイティブ化した画面のあと、毎回、同じルートで両方の動いているアプリを比べてください:

  • ウェブ版 — **agent-browser**で取得(Vercel Labs CLI):ルートをopen、アクセシビリティツリーをsnapshot --json、スクリーンショット。
  • ネイティブ版 — **argent**でシミュレーター操作:describe / debugger-component-treeで構造、flowで毎回のチェックをリプレイ。

内容と動作の対等性で判定 — ピクセルではなく:ネイティブ化した画面はもっとネイティブに見えるべき、同じに見えてはいけません。(DOM外枠の段階は例外 — あそこはウェブUIそのものなので、合っているべき)。感覚はネイティブの一部で、スクリーンショットでは測れません — トランジション・ジェスチャーがある画面は、短い動画を取ってください、静止画だけではなく(./references/native-patterns.md → 感覚参照)。このループは、使うツールについて意見を持っています:agent-browserやargentがなければ、ユーザーに聞いて進める前にインストール — 手作業スクリーンショットに頼らない。完全レシピとセットアップは./references/verify-on-device.md。

参考資料

  • ./references/false-friends.md — ウェブの言い方 → ネイティブの相当品 + 各々の落とし穴。ステップ3~5 と、言い方を学び直すウェブ開発者の参照先。
  • ./references/native-patterns.md — ウェブUXパターン → ネイティブデザイン変更(@expo/ui優先)。ステップ4のデザイン変更ガイド — OS由来に見える、リスキン(見た目変更)ではなく。
  • ./references/verify-on-device.md — 2つ
原文(English)を表示

Web to Native

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.

flowchart TD
    A1[1 · Assess: write the worklist] --> A2[2 · Scaffold Expo shell]
    A2 --> A3[3 · DOM-component shell<br/>· expo-dom · SHIP DAY ONE]
    A3 --> A4[4 · Strangle screens to native<br/>highest-value first · expo-router]
    A4 -->|more screens| A4
    A4 --> A5[5 · Wire data / auth / storage<br/>· expo-data-fetching]
    A5 --> A6[6 · Ship · eas-app-stores]

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 @expo/ui 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-router: NativeTabs, large titles), liquid glass and native components via @expo/ui, and mobile UX (sheets, swipe, haptics). The web→native pattern map is ./references/native-patterns.md. If it still feels like a website, you ported instead of redesigned.
  • 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.

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 ./references/run-as-goal.md for this app and present it; it re-reads this skill every iteration, so each /goal turn reloads the playbook + worklist and drives the next screen (it even self-bootstraps the assess step). Then run /goal 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.

The migration

No repo to migrate - just building native fresh as a web dev? You don't need these steps: use expo-router, and keep ./references/false-friends.md open for the web→native idiom map. Everything below assumes an existing web app.

1. Assess → write the worklist

Read the repo and produce migration-progress.md, the durable worklist the rest of the migration checks off. Make two cuts:

  • 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).
  • 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 (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.

2. Scaffold the shell

create-expo-app, then mirror the web routes in Expo Router — Next's tree maps almost 1:1 (note [id]/page.tsx → [id].tsx, and routes may live in src/app/). Empty screens, one per route.

3. Shell it in DOM components — the day-one milestone

Bring 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/link), carrying the styling over - all covered in false-friends. Then verify by running (below); this is shippable to TestFlight as-is.

4. Strangle screens to native — by value

Walk migration-progress.md top-down. For each screen, redesign it native - don't port the web layout. Reach for @expo/ui first (real SwiftUI/Compose - buttons, lists, sheets, pickers, sliders; ./references/native-patterns.md maps which web pattern becomes which native component), then platform navigation (expo-router - NativeTabs, large titles) and mobile UX (swipe, haptics, momentum/inverted scroll); RN primitives only for custom layouts. Consult ./references/false-friends.md for each idiom. @expo/ui 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 (./references/run-as-goal.md).

5. Wire data, auth, and storage

The 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.

6. Ship

eas-app-stores for the store builds (App Store / Play / TestFlight), EAS Update for OTA pushes after.

Verify by running, not compiling

A 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:

  • Web original — capture it with agent-browser (vercel-labs CLI): open the route, snapshot --json the accessibility tree, screenshot.
  • Native — drive the simulator with argent: describe / debugger-component-tree for structure, flow to replay the check each pass.

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 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 ./references/verify-on-device.md.

References

  • ./references/false-friends.md — web idiom → native equivalent + the gotcha for each. The lookup for steps 3–5, and for any web dev unlearning idioms.
  • ./references/native-patterns.md — web UX pattern → native redesign (@expo/ui-first). The step-4 redesign playbook so screens feel OS-native, not reskinned.
  • ./references/verify-on-device.md — the two-agent parity recipe: drive the web app (browser agent) and the native app (argent), open the same route, compare.
  • ./references/run-as-goal.md — a ready-shaped, migration-specific goal objective for driving step 4 unattended (re-reads this skill each iteration).
  • Expo — From Web to Native with React — the canonical guide this skill operationalizes.

Submitting Feedback

If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:

npx --yes submit-expo-feedback@latest --category skills --subject "expo-web-to-native" "<actionable feedback>"

Only submit when you have something specific and actionable to report. Include as much relevant context as possible. If an AI agent repeatedly failed or the user had to take over an Expo task, load the expo-skill-feedback skill and follow its eval-candidate flow instead of reusing the command above.

原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。