Diagnosing the Ionic + Capacitor White Screen After Splash on Real Devices
Diagnostic guide for the Ionic + Capacitor 'white screen after splash' failure on real devices. Covers symptoms, cause-to-diagnostic mapping, ordered remote debugging checks, fixes for base-href, JS errors, plugin sync, CORS, and escalation criteria for WebView or native crashes.
13 Sept 2025, 19:05 UTC

The Problem: Splash Screen Dismisses, Then Nothing
Your Ionic app builds without errors, installs on a device, shows the splash screen, and then the WebView stays blank white. The same code runs fine with ionic serve. That contrast — working in the browser, failing on device — is the key discriminator. The failure almost always stems from how assets load, how the router base path resolves, or a JavaScript error that only surfaces inside the Capacitor WebView.
Quick Cause & Diagnostic Mapping
| Symptom in Remote Inspector | Most Likely Cause | Where to Look First |
|---|---|---|
404 on main.js, polyfills.js, or chunk files | Wrong baseHref or absolute asset paths in index.html | index.html <base href> value |
Red JS error at startup (e.g., localStorage undefined, plugin not found) | Unhandled runtime error in WebView context | Console stack trace — note the failing module |
| No console errors, but network calls to API fail | CORS or mixed-content blocking from capacitor://localhost (iOS) or http://localhost (Android) | Network tab — check request origin and response headers |
Plugin method throws Plugin not installed or Capacitor not ready | Plugin missing from native project or called before Capacitor.isPluginAvailable() | Native project Pods / Gradle dependencies; call site timing |
| Clean console, white screen persists | WebView/GPU rendering issue or CSS layout collapse | Test with a minimal starter app; compare WebView versions |
Ordered Diagnostic Checks
1. Attach Remote Debugging and Read the Console
Do not guess — attach the inspector first.
- iOS: Enable Web Inspector in Settings → Safari → Advanced. Connect device, open Safari on Mac, choose Develop → [Your Device] → [Your App]. Requires a debug-signed build (not App Store / release profile).
- Android: Enable USB debugging. Run
chrome://inspectin Chrome desktop, find your app under Remote Target, click inspect.
What to look for: A red error at the very top of the console usually names the failing module or asset. A 404 on main..js or a chunk points directly to base-href/asset path issues. A ReferenceError or TypeError in a vendor bundle often indicates a missing polyfill or browser-only API (e.g., window.localStorage accessed in a Web Worker).
2. Verify the Built Output Matches What the WebView Loads
A stale synced bundle is a frequent cause after config or dependency changes.
- Confirm
webDirincapacitor.config.tsmatches your framework's actual output folder:- Angular:
www(defaultoutputPathinangular.json) - React (Vite):
dist(orbuildfor CRA) - Vue (Vite):
dist
- Angular:
- Rebuild web assets and sync:
Permissions: No elevated privileges needed. Risk:# Run in project root (where package.json lives) npm run build # or: ionic build npx cap sync # copies webDir → native projects, updates pluginscap syncoverwrites native project files inios/App/App/publicandandroid/app/src/main/assets/public— commit native changes before running if you've edited them manually. - Verify freshness: open the native project's public assets folder and confirm timestamps match your latest web build. On iOS:
ios/App/App/public. On Android:android/app/src/main/assets/public.
3. Isolate Network Failures
If the console shows failed API calls, the app origin is capacitor://localhost (iOS) or http://localhost (Android). Cross-origin rules apply.
- Quick test: Stub the failing API calls (return mock data) and reload. If the app renders, the issue is CORS/mixed-content.
- Fix options:
- Enable CORS on your API for the Capacitor origin (add
Access-Control-Allow-Origin: capacitor://localhostandhttp://localhost). - Use
@capacitor-community/httpplugin to bypass WebView CORS — it uses native networking stacks.
- Enable CORS on your API for the Capacitor origin (add
Fixes Tied to Findings
Base-href / Asset Path 404s
Capacitor serves from a local file scheme, not a server root. Absolute paths (/assets/...) or a root baseHref (/) break.
// index.html — use relative base
<base href="./">
// angular.json (Angular CLI)
"architect": {
"build": {
"options": {
"baseHref": "./"
}
}
}
// vite.config.ts (React/Vue)
export default defineConfig({
base: './',
// ...
});
After changing, rebuild and run npx cap sync again.
Startup JavaScript Exception
Common culprits:
localStorage/sessionStorageaccessed in a context where they're unavailable (e.g., inside a Web Worker or during SSR hydration mismatch).- Missing polyfill for
globalThis,process.env, orBuffer— add viapolyfills.ts(Angular) or Vitedefine/optimizeDeps.esbuildOptions.define. - Plugin method called before
Capacitor.isPluginAvailable('PluginName')returns true. Guard calls:import { Capacitor } from '@capacitor/core'; async function useCamera() { if (!Capacitor.isPluginAvailable('Camera')) return; const { Camera } = await import('@capacitor/camera'); // safe to call Camera.getPhoto() }
Plugin Not Installed / Native Sync Missing
After adding a plugin (npm i @capacitor/camera), you must run npx cap sync so the native project gets the podspec / Gradle dependency. On iOS, open ios/App/App.xcworkspace in Xcode and build once to compile the pod. On Android, a Gradle sync in Android Studio suffices.
Network / CORS Failures
If you cannot change the API server, switch to the native HTTP plugin:
npm i @capacitor-community/http
npx cap sync
// usage
import { Http } from '@capacitor-community/http';
const response = await Http.get({ url: 'https://api.example.com/data' });
This bypasses WebView CORS entirely because the request originates from native code.
Escalation Criteria
Stop app-level debugging and escalate when:
- Console is clean but rendering fails. Likely a WebView/GPU or CSS layout issue. Test by temporarily replacing
app-root(or your framework's root component) with a minimal static HTML page inindex.html. If that renders, the problem is in your component tree or styles. - Failure only on specific OS versions. Check the WebView/Chromium version on Android (
chrome://inspectshows it) and iOS WKWebView version (tied to iOS version). Search the Capacitor/Ionic issue trackers for that version. - Native crash instead of JS error. Device logs show native stack traces:
- iOS: Xcode → Devices and Simulators → View Device Logs, or
idevicesyslog. - Android:
adb logcat *:Eor Android Studio Logcat filtered to your package.
- iOS: Xcode → Devices and Simulators → View Device Logs, or
Limitations & Verification Checklist
- Platform differences: iOS uses WKWebView; Android uses Chromium WebView. A fix verified on one must be re-verified on the other.
- Capacitor version sensitivity: Configuration keys (
server.hostname,allowNavigation, scheme defaults) changed across major versions. Confirm behavior against the major version installed (npx cap --version). - Release builds are not inspectable: Remote debugging requires a debug-signed build. If the white screen only appears in a release build, test with a debug build first.
ionic serveis not proof: Browser origin, available APIs, and module loading differ from the device WebView.
Verification Steps After Each Fix
- Rebuild web assets:
npm run build - Sync native projects:
npx cap sync - Reinstall on device (clean install preferred):
npx cap run ios --target=ornpx cap run android --target= - Attach remote inspector, confirm console is clean and app renders past splash.
- Cross-check on the other platform (iOS ↔ Android) to rule out platform-specific WebView causes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.