RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-22中級

Rork のスプラッシュ画面が消えない・起動直後に白い画面が出る問題の直し方

Rork で作ったアプリのスプラッシュ画面が長時間表示されたまま止まる・起動直後に白い画面が一瞬チラつく・iOSとAndroidで見え方が違う、といった症状の原因と具体的な直し方を、実機で検証したコードとあわせてまとめました。

Rork515Expo149トラブルシューティング77スプラッシュ画面React Native209

Rork で出力したアプリを実機で動かしたとき、スプラッシュ画面が数秒以上残って不安になる、あるいはスプラッシュが消えた直後に真っ白な画面が一瞬差し込んでから画面が表示される、という相談をよくいただきます。軽微に見えますが、App Store のレビューアーはアプリ起動時の第一印象を厳しく見るため、ここで引っかかってリジェクトされるケースも実際にあります。

症状1: スプラッシュが数秒消えずに止まって見える

もっとも多いのがこれです。原因はほぼ1つで、「SplashScreen.hideAsync() を呼ぶ前に、ビジネスロジックの初期化(ログイン状態の復元・フォント読み込み・リモート設定の取得など)を直列で待たせすぎている」ことです。

Rork の初期テンプレートは以下のようなコードを生成します。一見問題なさそうに見えて、実は await が詰まりがちです。

// ❌ この書き方だと、fetchRemoteConfig が遅いと splash が何秒も残る
import { useEffect } from "react";
import * as SplashScreen from "expo-splash-screen";
import { Stack } from "expo-router";
import { useFonts } from "expo-font";
 
SplashScreen.preventAutoHideAsync();
 
export default function RootLayout() {
  const [fontsLoaded] = useFonts({
    Inter: require("../assets/fonts/Inter.ttf"),
  });
 
  useEffect(() => {
    (async () => {
      if (!fontsLoaded) return;
      await fetchRemoteConfig(); // ここが3〜5秒かかる想定
      await restoreSession();
      await SplashScreen.hideAsync();
    })();
  }, [fontsLoaded]);
 
  return <Stack />;
}

この書き方だと、ネットワークが遅い環境や機内モード直後の起動で、ユーザーからは「アプリが固まっている」ように見えます。修正のポイントは「スプラッシュを隠すトリガーを、UI を描画できる最小条件にする」ことです。

// ✅ UI を出せる最低限が揃ったら即 hide、重い処理は裏で継続
import { useEffect, useState } from "react";
import * as SplashScreen from "expo-splash-screen";
import { Stack } from "expo-router";
import { useFonts } from "expo-font";
 
SplashScreen.preventAutoHideAsync().catch(() => {});
 
export default function RootLayout() {
  const [fontsLoaded] = useFonts({
    Inter: require("../assets/fonts/Inter.ttf"),
  });
  const [appReady, setAppReady] = useState(false);
 
  useEffect(() => {
    if (!fontsLoaded) return;
    // 1) UIを描画するのに必須なものだけ待つ(= 何もない)
    setAppReady(true);
    // 2) 重い処理はバックグラウンドで継続
    fetchRemoteConfig().catch((e) => console.warn("remote config失敗:", e));
    restoreSession().catch((e) => console.warn("session復元失敗:", e));
  }, [fontsLoaded]);
 
  useEffect(() => {
    if (appReady) SplashScreen.hideAsync().catch(() => {});
  }, [appReady]);
 
  return <Stack />;
}

リモート設定やセッション復元が終わっていなくても、最初のスクリーンは「ゲスト状態・デフォルト設定」で表示できる設計にしておくのがコツです。どうしても認証済みの画面しか出せないサービスであれば、<Stack /> の代わりに軽量なローディング画面を1枚挟むと、スプラッシュがずっと残るよりユーザー体験が良くなります。

症状2: スプラッシュ直後に白い画面がチラつく

これは SplashScreen.hideAsync() を呼んだタイミングと、ルート画面が描画されるタイミングの間に隙間ができている状態です。特にダークテーマのアプリで目立ちます。

確認すべきは app.jsonbackgroundColor と、expo-splash-screen のコンフィグが食い違っていないかです。Rork が出力するテンプレートはしばしば両者が不一致になっています。

// app.json(抜粋)
{
  "expo": {
    "backgroundColor": "#0B0F14",        // ← ルートViewの背景
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#0B0F14"       // ← splashの背景。ここを合わせる
    },
    "ios": {
      "backgroundColor": "#0B0F14"
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#0B0F14"
      }
    }
  }
}

3つの backgroundColor(ルート・splash・プラットフォーム別)をすべて同じ色にしておくと、スプラッシュが消えた瞬間の白フラッシュが見えなくなります。ライトテーマでも、ホーム画面の背景色と一致させる原則は同じです。

さらに、expo-router のルート _layout.tsx に背景色を明示しておくと保険になります。

import { View } from "react-native";
import { Stack } from "expo-router";
 
export default function RootLayout() {
  return (
    <View style={{ flex: 1, backgroundColor: "#0B0F14" }}>
      <Stack screenOptions={{ contentStyle: { backgroundColor: "#0B0F14" } }} />
    </View>
  );
}

contentStyle を指定しないと、ナビゲーション遷移の一瞬に React Navigation のデフォルト白背景が透けます。ここで詰まる方が意外に多い箇所です。

症状3: iOS はきれいだが Android だけスプラッシュの画像がずれる

Android 12 以降は SplashScreen API の仕様変更があり、従来の full-screen の画像ではなくアプリアイコン中心の円形スプラッシュに置き換えられています。Rork の初期テンプレートにはこの差分が反映されていないことがあり、Android だけ画像が中央の小さな円に切り抜かれる現象が起きます。

app.json に Android 用の追加設定を入れておくと、端末側の挙動に合わせて調整できます。

{
  "expo": {
    "plugins": [
      [
        "expo-splash-screen",
        {
          "image": "./assets/splash.png",
          "resizeMode": "contain",
          "backgroundColor": "#0B0F14",
          "imageWidth": 200
        }
      ]
    ],
    "android": {
      "backgroundColor": "#0B0F14",
      "splash": {
        "image": "./assets/splash-android.png",
        "resizeMode": "cover",
        "backgroundColor": "#0B0F14"
      }
    }
  }
}

私の経験では、スプラッシュ用の画像は「iOS 用にフル解像度(1284×2778px 程度)」と「Android 12+ 用に中央配置の小さめロゴ(1024×1024px・余白多め)」の2枚を用意しておくのが一番失敗しません。同じ1枚を使い回すと、Android 12 以降で円形マスクがかかったときにロゴが大きく欠けてしまいます。

症状4: ダークモードにするとスプラッシュだけ白いままになる

iOS は UIUserInterfaceStyle を設定しないと、端末がダークモードでもスプラッシュは常にライトモードで表示されます。Android も android:theme を分岐させないと同じ現象になります。

Expo では app.json に以下を記述するだけで両プラットフォームのダーク対応が入ります。

{
  "expo": {
    "userInterfaceStyle": "automatic",
    "ios": {
      "userInterfaceStyle": "automatic"
    },
    "android": {
      "userInterfaceStyle": "automatic"
    },
    "splash": {
      "image": "./assets/splash-light.png",
      "backgroundColor": "#ffffff",
      "dark": {
        "image": "./assets/splash-dark.png",
        "backgroundColor": "#0B0F14"
      }
    }
  }
}

splash.dark が効くのは expo-splash-screen を config plugin 経由で使っている場合に限ります。Rork が吐き出す app.json にこのキーがなかったら、上記のように手で追記してください。設定後は必ず npx expo prebuild --clean を実行して native プロジェクトに反映させます。これを忘れると、JS 側だけ書き換えても古いネイティブリソースが残り、ダーク時の背景色が変わりません。

症状の切り分けに使える確認ステップ

最後に、どのパターンに該当するかを判断する簡易フローを置いておきます。

  • スプラッシュ表示時間が3秒以上 → 症状1(初期化待ち)を疑う
  • スプラッシュが消えた直後の一瞬だけ白い → 症状2(背景色不一致)を疑う
  • Android 12+ の実機のみロゴが小さい・切れる → 症状3(Android 12 仕様)を疑う
  • ダークモードの端末だけ白背景 → 症状4(dark キー未設定)を疑う

切り分けのために便利なのが、スプラッシュ表示時間の可視化です。_layout.tsx の先頭に以下を仕込んで、実機で何秒かかっているか数値で見ると、症状1なのか別のレイヤーの問題なのかが一目でわかります。

const bootStart = Date.now();
 
useEffect(() => {
  if (appReady) {
    console.log("splash shown:", Date.now() - bootStart, "ms");
    SplashScreen.hideAsync().catch(() => {});
  }
}, [appReady]);

500ms 以下なら実質ゼロ体感、1秒を超えると遅いと感じ始め、2秒を超えると「固まっている」と認識されます。この数値をチームで共有しておくと、レビュー前のチェックがぶれなくなります。

実際にハマって時間を溶かした小ネタ

本編に入りきらなかったのですが、自分が過去に時間を溶かした小ネタを3つだけ残しておきます。

1つ目は iOS シミュレーターの splash 画像キャッシュです。splash.png を差し替えてリビルドしても古い画像が残ることがあります。シミュレーターの「Device → Erase All Content and Settings」で初期化し、アプリも一度削除してから npx expo run:ios を走らせ直してください。プロジェクト側の expo prebuild --clean ではシミュレーター側のキャッシュまでは消せません。

2つ目は expo-updates の OTA アップデートが splash を固める件です。起動時の Updates.checkForUpdateAsync() が遅いと、splash の自動非表示も連動して止まります。チェックに上限時間を設けるのが手堅いです。

await Promise.race([
  Updates.checkForUpdateAsync(),
  new Promise((resolve) => setTimeout(resolve, 2000)),
]);

3つ目は Android の端末メーカー独自 UI(One UI や MIUI など)が、アプリのスプラッシュ前に OEM 独自のスプラッシュを挟むことです。コード側では除去できませんが、背景色を OEM の表示と揃えておくと違和感がかなり減ります。

直したあとの最終確認

ここまでの修正を反映したら、npx expo prebuild --clean で native プロジェクトを作り直し、以下の3環境で確認してください。エミュレーターだけだと再現しない差分が意外と多いです。

  • iOS 実機のライト / ダーク両モード
  • Android 12 以降の実機(Pixel または Galaxy)のダークモード
  • 機内モードでの起動(ネットワーク待ちが発生するケース)

スプラッシュ画面は一度整えると半年以上触らない場所なので、今日のうちに4症状を潰しておくと、後々のリリース作業が軽くなります。関連する画面周りの不具合についてはRork アプリで起動直後にクラッシュして白画面になる問題の直し方Rork アプリのローディングが止まる問題のデバッグ手順も合わせて読むと、起動フロー全体の理解が深まります。スプラッシュの意匠そのものを設計から見直したい方にはRork と Expo のスプラッシュ画面 完全設計ガイドが役立つはずです。

まずは一番気になっている症状に絞って、原因特定のための console.log を1行足すところから始めてみてください。数値で見えるようになった瞬間に、どこを直せばいいかが一気にクリアになります。

シェア

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

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

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

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

関連記事

開発ツール2026-05-25
Rork で targetSdkVersion 35 にしたら Android のレイアウトが下まで埋まる問題の直し方
Android 15 (API 35) で強制されるエッジ・トゥ・エッジ表示によって、Rork で生成したアプリの下部タブやヘッダーがシステムバーに食い込む問題を、react-native-edge-to-edge と useSafeAreaInsets を使って実装パターン別に解決します。
開発ツール2026-04-20
Rork 生成コードで useEffect が無限ループする原因と直し方
Rorkで生成したReact NativeコードのuseEffectが無限ループする原因を5パターン解説。依存配列の設定ミス・オブジェクト依存・setState競合など具体的なBefore/Afterコードで根本修正する方法をまとめています。
開発ツール2026-04-15
Rorkアプリでキーボードが入力フィールドを隠す問題の完全対処ガイド
Rorkアプリでキーボードが表示されるとテキスト入力フィールドが隠れる問題の原因と解決策を解説します。KeyboardAvoidingView・ScrollView・useKeyboard フックを使った実践的な対処法を紹介します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →