Skip to content

Blank white screen on cold load in Edge/iOS (iOS-26 WKWebView render freeze) — paused, likely upstream WebKit bug #224

@jasoneplumb

Description

@jasoneplumb

Summary

On Edge (and other third-party browsers) on iPhone, a fresh cold load shows a blank white screen ~50% of the time. The page is fully loaded and the JavaScript runs — the browser just never paints the content to the screen. Only a full reload clears it (scrolling, tapping, and rotating do not). Safari on the same device is unaffected.

This issue documents an extensive investigation (8 releases, v0.34.2-beta → v0.34.8-beta) that ruled out every web-reachable cause. The conclusion is that this is a render-process / compositing freeze in iOS-26 WKWebView (the engine all third-party iOS browsers are required to use), almost certainly a WebKit regression on a brand-new OS. It is paused pending a future iOS update; Safari is the workaround.

Environment

  • Device: iPhone, iOS 26.5
  • Browser: Microsoft Edge for iOS (EdgiOS/148.0.3967.97), which runs on WKWebView (AppleWebKit/605.1.15, Version/26.0)
  • Reproduced by: clearing all browsing data, then cold-loading the site repeatedly (~3–5 blanks per 7–9 loads).
  • Safari on the same iOS 26 device does NOT reproduce it.

Decisive on-device diagnostic

A temporary on-screen probe (since removed) captured the page state on a blank/parked load:

bundleRan=true        ← the JS bundle loaded and executed
readyState=complete   ← document fully loaded
mapChildren=0
loadedTiles=0
hasConsent=false      ← parked at the consent gate (expected — see note)
consentOverlay=393x661 ← on loads that DO paint, the overlay is in the DOM at full size
swController=no        ← service worker NOT controlling the page
ua=…EdgiOS/148… iPhone OS 26_5 … AppleWebKit/605.1.15

Key facts established:

  1. The app is healthy. bundleRan=true, readyState=complete, DOM present and correctly sized. Nothing is throwing.
  2. It's a whole-page paint failure, not any single element. On a blank load, neither the consent overlay nor the separate inline diagnostic overlay paints — two unrelated elements injected into document.body, both invisible. Even appending a fresh full-screen element several seconds later does not paint.
  3. The service worker is not involved. swController=no.
  4. It is undetectable from JavaScript. The Paint Timing API records a first-contentful-paint entry even on frozen loads (the paint is computed but never composited to screen), so the page cannot detect its own freeze to auto-recover.
  5. Only a fresh navigation (reload) clears it — scroll / tap / rotate (which force relayout+repaint) do not, indicating the freeze is below the layout/paint-trigger level (render/GPU process).
  6. Safari works; only WKWebView freezes — same WebKit engine, so the differentiator is WKWebView's render/GPU-process behavior, which is more fragile under first-load conditions on iOS 26.

Note on hasConsent=false: the soak workflow clears data each run and refreshes past blanks without accepting the consent modal, so nearly every test load sits at the consent gate. The freeze is independent of the consent modal (proven by the diagnostic overlay also failing to paint).

What was tried and ruled out

Release PR Theory Result
(pre) #210 nginx served /sw.js with 1-year immutable cache → stale SW Real bug, fixed — but not the blank cause
v0.34.3-beta #213 Cache-first navigateFallback served empty navigations in WKWebView No effect
v0.34.4-beta #215 NetworkFirst networkTimeoutSeconds: 3 fell back to flaky cache No effect
v0.34.5-beta #217 Migrate generateSW→injectManifest: bulletproof nav (network→precache fallback) + SW-side error capture + purgeOnQuotaError No effect; SW-diag confirmed handler not even involved
v0.34.6-beta #219 Consent overlay injected during first paint; mount in requestAnimationFrame No effect (slightly worse: 5/9); reverted
v0.34.7-beta #221 Auto-reload via Paint Timing when no first-contentful-paint recorded Detector never fires — FCP is recorded despite the freeze
v0.34.8-beta #223 Strip first-paint compositing layers: remove #map { isolation: isolate } (full-screen composited layer) + lazy-inject #offline-banner (fixed+transform) No effect (~50% unchanged)

Conclusion: the cause is not the service worker, not the consent modal, and not first-paint compositing pressure from app CSS. It is not auto-recoverable (JS cannot detect a compositing freeze) and not preventable by the levers available to web code.

Recommendation / next steps

  • Treat as an upstream iOS-26 WKWebView (WebKit) regression. iOS 26 was released days before this investigation; render-process regressions on new OS releases are common and typically fixed in point updates.
  • Workaround for users: use Safari (unaffected), or install the site to the Home Screen (runs in a system WebKit context with Safari-like behavior).
  • Revisit when: a new iOS 26.x point release ships — re-test on the updated OS before spending more effort.

If revisiting and the freeze persists on a newer iOS, ideas not yet tried (all low-confidence):

  • Test whether it reproduces in Chrome for iOS and a minimal/blank test page hosted on the same origin — to determine whether it's app-specific at all or any WKWebView page on this OS.
  • A minimal reproduction (static HTML, no Vite/Leaflet) to file an Apple Feedback / WebKit bug with.
  • Inline a non-white <body> background so a freeze degrades to a branded screen rather than stark white (cosmetic only, not a fix).
  • Check whether display: standalone / the web app manifest or theme-color interacts with the freeze.

Related

Diagnostics that enabled this investigation: #206 (boot-watchdog), #207/#208 (on-screen blank-state reporter). The temporary diagnostics and the (exonerated) service-worker complexity are being removed in a separate clean-up PR; this issue preserves the full record.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingwontfixThis will not be worked on

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions