Diagnosing White Screen of Death in Ionic Capacitor Apps
A step‑by‑step diagnostic guide for the white screen of death in Ionic Capacitor apps, covering JS bootstrap errors, base href misconfiguration, CSP blocks, plugin mismatches, and root‑component freezes with concrete commands and verification steps.
16 Oct 2025, 08:54 UTC

Recognizable condition
The app launches on a physical device or emulator, shows a splash screen, then displays a completely white viewport with no UI elements. No error toast appears, and the JavaScript console is silent when viewed from a desktop browser.
Common causes at a glance
| Symptom | Typical root cause | Where to look first |
|---|---|---|
| White screen immediately after splash | JavaScript runtime error during bootstrap | Chrome DevTools / Safari Web Inspector console |
| White screen only on deep‑link or sub‑path URLs | Incorrect base href in index.html | index.html and build output paths |
White screen on device but works in ionic serve | Content Security Policy blocks scripts or XHR | meta http-equiv="Content-Security-Policy" tag |
| Crash before any JS runs | Native plugin version mismatch or missing permission | Xcode / Android Studio logcat |
| UI freezes after splash, no console output | Infinite loop or heavy work in root component constructor | Root component constructor / ngOnInit |
Ordered diagnostic checks
Attach a remote debugger
Run the app on a connected device with live reload disabled so the production bundle loads:
ionic capacitor run android --prod --no-livereload # or for iOS ionic capacitor run ios --prod --no-livereloadThen open
chrome://inspect(Android) or Safari → Develop → [Device] → [App] (iOS). Look at the Console tab for any red error lines. Expected check: a stack trace pointing to a specific file and line number. Risk: enabling--proddisables source maps; keep a debug build handy for mapping.Validate
base hrefOpen
src/index.html(or the generatedwww/index.html) and confirm the<base href="/">matches the deployment path. If the app is hosted under/myapp/, the tag must be<base href="/myapp/">. Rebuild after any change:ionic build --prod ionic capacitor copy android ionic capacitor copy iosExpected check: network tab shows 200 for all
.jsand.cssrequests. Risk: an incorrect base href produces 404s that are silent on the console because the bootstrap script never loads.Inspect Content Security Policy
Locate the CSP meta tag in
index.html. A typical strict policy looks like:<meta http-equiv="Content-Security-Policy" content="default-src 'self' https: data:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;>Remove
'unsafe-inline'and'unsafe-eval'only if you have no inline scripts oreval()usage. Test by temporarily relaxing the policy todefault-src * 'unsafe-inline' 'unsafe-eval';and see if the white screen disappears. Expected check: console shows CSP violation reports pointing to blocked resources. Risk: overly permissive CSP weakens security; restore a strict policy after identifying the blocked asset.Check native plugin compatibility
Run the native IDE and watch the device log while the app starts:
# Android adb logcat -s "Capacitor","WebView","Chromium" # iOS (in Xcode console filter by the app name)Look for lines such as
Plugin "Camera" not foundorTypeError: Cannot read property 'getPhoto' of undefined. Verify that the plugin’s JavaScript package version matches the native pod / Gradle dependency. Example:@capacitor/camera@5.0.0requiresCapacitorCamerapod version 5.x. Update both sides:npm install @capacitor/camera@latest ionic capacitor update android ionic capacitor update iosExpected check: no plugin‑related crashes in native logs. Risk: updating plugins may introduce breaking API changes; run the test suite after each upgrade.
Audit root component initialization
Open
src/app/app.component.ts(or the framework‑specific root). Ensure the constructor does not perform async work, heavy calculations, or subscribe to observables that never complete. Move any initialization tongOnInitor a dedicatedinitializeApp()method called afterplatform.ready().constructor(private platform: Platform) { this.platform.ready().then(() => this.initializeApp()); } private async initializeApp() { // safe to call plugins here await SplashScreen.hide(); }Expected check: the console shows
Platform readyand no “Maximum call stack size exceeded” or long‑running script warnings. Risk: moving code out of the constructor can change lifecycle timing; verify that any guards or route resolvers still receive required data.
Fixes tied to findings
- JS bootstrap error – fix the reported syntax/type error, rebuild, and redeploy.
- Base href mismatch – correct the tag, rebuild, and run
capacitor copyfor each platform. - CSP violation – add the blocked domain or hash to the policy; avoid
eval()and inline scripts in production. - Plugin mismatch – align JS and native versions, then run
capacitor sync. - Root component blockage – refactor heavy work out of the constructor; use
platform.ready()guard.
Escalation criteria
Escalate to a platform‑specific debugging session when:
- No JavaScript errors appear in the remote console, yet the native logs show a WebView crash (e.g.,
SIGSEGVon Android orEXC_BAD_ACCESSon iOS). - All CSP, base href, and plugin checks pass, but the screen remains white on multiple devices.
- The issue reproduces only on a specific OS version (e.g., Android 14 WebView 115) indicating a WebView bug.
In those cases, capture a full native crash dump (Android: adb bugreport; iOS: Xcode → Devices → View Device Logs) and file an issue with the Capacitor or WebView maintainers.
Quick verification checklist
- Remote console shows zero errors after fix.
- Network tab loads all bundles with 200 OK.
- CSP reports no violations in the console.
- Native logs show clean WebView start‑up.
- App renders the first page within 2 seconds on a cold start.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.