◉RORK LABEN
●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…●GPT-6.1 SOL — Rork のモデルメニューに追加(09-29)。Low/Medium/High を選べ、Pro・Max に含まれます●SONNET 5.5 — UI づくりが得意で、Sonnet 5 より出力が速いモデルが選べます(09-28)●SDK 58 — Beta が 09/15 から。stable の案内はまだなく、Expo Go でのプロジェクト実行は 09/03 からログインが必要です●WIDGET — iOS のウィジェットが本番ビルドだけ真っ黒になる原因を追った報告が出ています●OBSERVE — EAS Observe がネイティブクラッシュも記録します(SDK 57.0.21 以降・10/07)●NEW — 起動広告が、背面から戻った回にしか出ていませんでした。「前面に戻った」合図が、…
記事一覧/開発ツール
⬡ 開発ツール/2026-04-24中級

Rork アプリの WebView が真っ白になる・ページが表示されないときに確認する 6 つの原因

RorkアプリのWebViewが真っ白になる、ページが読み込まれないときに確認する6つの原因です。iOSのATS、AndroidのcleartextTraffic、リダイレクトとクッキーの無限ループ、プロセス外WebViewのクラッシュ、プレビューと本番の差まで切り分けて直します。

Rork585WebViewトラブルシューティング85React Native239iOS115Android55

Rork で作ったアプリに「社内ツールの画面を WebView でそのまま埋め込みたい」「外部サービスのログインを一時的に WebView で挟みたい」といった要件を入れた途端、シミュレーターでは動いていたのに実機で真っ白、ということがよくあります。私自身も、プレビュー動画用に急いで組み込んだ WebView が Android の実機で一切描画されず、リリース直前に半日潰した苦い記憶があります。

WebView の「真っ白」はエラーが前面に出ないぶん原因の切り分けが難しいのですが、実際に起きる詰まり方には明確なパターンがあります。ここでは Rork で react-native-webview を使う前提で、現場でよく遭遇する 6 つの典型パターンを上から順に潰していく流れで整理します。

まずは「どこで止まっているか」を切り分ける

真っ白に見えるとき、実は 3 つの異なる状態が混ざっています。

  • ネイティブ側の WebView コンポーネント自体が画面に乗っていない
  • WebView は乗っているが URL がロードに失敗している
  • URL は読み込めているが HTML 側が空、または JavaScript で描画待ちのまま止まっている

この切り分けを最初にやるかどうかで、その後の作業時間が 1 時間変わります。いちばん手堅いのは、onLoadStart / onLoad / onError をすべて仕込んで、どのイベントが発火しているかをログに出してしまうことです。

// components/DebugWebView.tsx
import { WebView, type WebViewNavigation } from "react-native-webview";
import { View, Text, StyleSheet } from "react-native";
import { useState } from "react";
 
type Props = { uri: string };
 
export default function DebugWebView({ uri }: Props) {
  const [status, setStatus] = useState<"idle" | "loading" | "loaded" | "error">("idle");
  const [lastUrl, setLastUrl] = useState<string>("");
  const [errorText, setErrorText] = useState<string>("");
 
  return (
    <View style={styles.container}>
      {/* ステータスバー:ユーザーには見せず、開発中のみ描画する */}
      <Text style={styles.status}>
        status: {status} / url: {lastUrl}
        {errorText ? ` / error: ${errorText}` : ""}
      </Text>
      <WebView
        source={{ uri }}
        style={styles.webview}
        onLoadStart={(e) => {
          setStatus("loading");
          setLastUrl(e.nativeEvent.url);
        }}
        onLoad={() => setStatus("loaded")}
        onError={(e) => {
          setStatus("error");
          setErrorText(e.nativeEvent.description ?? "unknown");
        }}
        onNavigationStateChange={(nav: WebViewNavigation) => {
          // 遷移が起きた瞬間の URL を確認できる
          setLastUrl(nav.url);
        }}
      />
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: { flex: 1 },
  status: { padding: 8, backgroundColor: "#111", color: "#0f0", fontSize: 12 },
  webview: { flex: 1 },
});

このデバッグビューを 1 分だけ挟むと、「loading のまま止まっている」のか「error になっている」のか、「loaded なのに真っ白」なのかが一目で分かります。以降の原因特定はこの結果をもとに進めます。

原因 1:iOS の App Transport Security(ATS)で HTTPS が必須

iOS 実機で真っ白になり、シミュレーターでも onError が発火するのに Android では動く ─ これはほぼ確実に ATS による HTTP ブロックです。http:// で始まる URL は iOS 側で黙ってブロックされ、何のダイアログも出ません。

Rork 経由でエクスポートした Expo プロジェクトの場合、app.json の ios.infoPlist に例外を書くか、先方の URL を HTTPS に切り替えてもらうのがまっとうな解決策です。社内サーバーなど一時的に HTTP を許容したい場合は、次のように最小限のドメイン単位で許可するとリジェクトリスクを抑えられます。

{
  "expo": {
    "ios": {
      "infoPlist": {
        "NSAppTransportSecurity": {
          "NSAllowsArbitraryLoads": false,
          "NSExceptionDomains": {
            "staging.example.internal": {
              "NSTemporaryExceptionAllowsInsecureHTTPLoads": true,
              "NSTemporaryExceptionMinimumTLSVersion": "TLSv1.0",
              "NSIncludesSubdomains": true
            }
          }
        }
      }
    }
  }
}

NSAllowsArbitraryLoads を true にする解説記事も見かけますが、App Store 審査で理由を問われることがあるので、対象ドメインだけを NSExceptionDomains で開ける形を強くおすすめします。HTTPS 化・証明書まわりで詰まったときは、API 通信側と同じ原因であることも多いので、ネットワークリクエストのデバッグ手順 もあわせて確認すると早いです。

原因 2:Android の cleartextTraffic と Mixed Content

Android 9(API 28)以降は HTTP 通信がデフォルトで無効になっています。iOS と同じように、HTTPS が使えるならそれが最良ですが、社内 URL や開発用の IP アドレス(http://192.168.x.x)を開きたい場面は現実に残ります。

Expo 管理下のプロジェクトでは、app.json の android.usesCleartextTraffic を true にすることでまず扉を開けます。そのうえで、react-native-webview 側では mixedContentMode を明示しておかないと、HTTPS ページ内の HTTP リソース(画像や iframe)が読み込まれず、結果として中身がスカスカになります。

<WebView
  source={{ uri: "http://192.168.10.5:3000" }}
  mixedContentMode="compatibility" // "always" / "compatibility" / "never"
  javaScriptEnabled
  domStorageEnabled
  // 本番で使う場合は該当 URL のみに限定する
/>

mixedContentMode="always" を本番ビルドにそのまま残すとセキュリティ上よくないので、開発ビルドと本番ビルドで分けておくのが無難です。EAS のプロファイル別に環境変数で切り替える運用が、Rork で作ったアプリでもいちばん扱いやすいと感じます。

原因 3:リダイレクトとクッキーでログイン画面が無限ループ

外部サービスの OAuth や SAML を WebView で踏ませたとき、「ログインボタンを押した直後に最初の画面に戻る」「ずっとローディングのまま」になることがあります。これはクッキーが WebView 内で保持されていない、またはリダイレクト先が about:blank に化けているケースがほとんどです。

最低限押さえておきたいのは以下の 3 点です。

  • sharedCookiesEnabled を true にして、iOS のネイティブクッキーストアと WebView を共有する
  • thirdPartyCookiesEnabled を Android で true にしておく(iOS 14 以降の ITP 相当の挙動に注意)
  • onShouldStartLoadWithRequest でカスタムスキーム(yourapp://callback)をフックしてネイティブ側へ戻す
<WebView
  source={{ uri: loginStartUrl }}
  sharedCookiesEnabled
  thirdPartyCookiesEnabled
  incognito={false}
  onShouldStartLoadWithRequest={(req) => {
    if (req.url.startsWith("yourapp://callback")) {
      // カスタムスキームを検出したらネイティブ側の認証完了処理へ渡す
      handleAuthCallback(req.url);
      return false; // WebView には遷移させない
    }
    return true;
  }}
/>

onShouldStartLoadWithRequest を仕込まずにカスタムスキームを渡すと、WebView が「開けない URL」と判断して白画面になることがあります。OAuth 絡みで真っ白になるときは、まずここを疑うのが近道です。

原因 4:JavaScript は動いているのに描画待ちで止まっている

「onLoad まで発火したのに真っ白」というパターンは、SPA 側が window.requestIdleCallback やフォントロードを待ってから描画している、もしくは WebView の viewport メタタグがなくて縦横比がおかしくなっているだけ、というのが定番です。

確認は injectedJavaScriptBeforeContentLoaded で document.readyState と document.body.innerHTML.length を通知させるのが速いです。

<WebView
  source={{ uri }}
  onMessage={(e) => console.log("[webview]", e.nativeEvent.data)}
  injectedJavaScriptBeforeContentLoaded={`
    window.addEventListener('DOMContentLoaded', () => {
      window.ReactNativeWebView.postMessage(JSON.stringify({
        ready: document.readyState,
        bodyLength: document.body ? document.body.innerHTML.length : 0,
        title: document.title,
      }));
    });
    true; // React Native 側で評価結果を undefined にするおまじない
  `}
/>

bodyLength がそれなりの数字なのに画面が真っ白なら、CSS の height: 100vh が iOS の Safari 同等で計算されている、あるいはダークテーマで背景が黒のまま文字も黒になっている、という見落としが疑われます。

なお表示は出るのに画像だけ出ない、という症状なら原因はまた別で、画像・メディアの読み込みエラー対処 に網羅的にまとめてあります。

原因 5:プロセス外 WebView のクラッシュ(Android 限定)

Android では 2023 年以降、WebView がアプリ本体と別プロセスで動くようになり、メモリ不足や System WebView の更新中に勝手に「真っ白+無反応」状態になることがあります。onRenderProcessGone が実装されていないと、その瞬間アプリ側は何も知りません。

対策はハンドラを書いてリロードする、これだけです。

<WebView
  source={{ uri }}
  onRenderProcessGone={(e) => {
    console.warn("[webview] render process gone", e.nativeEvent);
    // 自動リロード、またはエラー画面に切り替え
    webRef.current?.reload();
  }}
/>

体感としては低価格帯の Android 端末で再現しやすく、レビュー欄に「急に画面が動かなくなる」と書かれる症状の多くがこれです。気づかずに放置すると星が削られるので、出荷前に一度仕込んでおく価値があります。長時間使うアプリで頻発する場合は、より広いクラッシュ対策を扱った アプリが実機でクラッシュするときの切り分け も参考にしてください。

原因 6:Rork プレビュー特有の「Companion では見えるのに本番で出ない」

Rork で開発中、Companion 経由のプレビューでは完璧に動いていたのに、TestFlight ビルドだと WebView だけ真っ白 ─ これは Companion が一部のネイティブ設定を甘めに上書きしているためです。

チェックすべきは以下の順序です。

  • app.json の plugins 配列に react-native-webview が追加されているか
  • eas.json の開発プロファイルと本番プロファイルで usesCleartextTraffic / infoPlist の値がずれていないか
  • 本番ビルドは Release 用の ATS ルールが適用される。Debug 用に緩めていた例外が Release に入っていない可能性がある

eas build --profile preview --platform ios でプレビューに近いビルドを作り、そのビルドで再現するかを確認すると、Companion 固有か本番固有かを切り分けられます。

次に何をするか

WebView の「真っ白」はエラーが見えにくいだけで、切り分けるイベントを 1 本仕込めば原因は必ず特定できます。いま手元のアプリで真っ白になっている方は、まず上の DebugWebView コンポーネントをそのまま貼り付け、status と errorText がどう遷移するかを 1 回観察してみてください。そこから原因 1 〜 6 のどの節に飛べばよいかが決まります。

WebView を使い込むなら、React Native と Expo のネットワーク周りの知識が土台になります。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⬡ 開発ツール2026-05-18
Rork で Linking.canOpenURL が iOS だけ false を返す ─ LSApplicationQueriesSchemes 設定漏れの直し方
TikTok や X、LINE 公式アカウントへ飛ばす実装が iOS の本番ビルドだけで動かない症状を、LSApplicationQueriesSchemes の追加と EAS Build の再生成手順で確実に解消します。
⬡ 開発ツール2026-10-07
日本語の見出しだけ太字にならない — Android で fontWeight が効かないときの切り分けと直し方
Rork で作ったアプリで、iPhone では太字なのに Android では日本語の見出しが細いままになる症状の切り分け記事です。原因の見分け方、fontFamily を重みごとに分ける直し方、確認用コードを載せています。
⬡ 開発ツール2026-09-21
exit code 0 で止まるビルド — ログを黙らせていた設定と、空ファイルを通した存在チェック
eas build --local が exit code 0 but produced no further output で止まる件と、0 バイトのスタブが埋め込まれず dyld で落ちる件。どちらも原因は出力を減らす設定でした。自分のスクリプトを棚卸しする手順まで書き残します。