A link or window.open inside a WebView does not open a new tab — it does nothing, silently. On Android the default is setSupportMultipleWindows(false), which ignores every request to open a new window; to make it work the app must enable multiple windows and implement onCreateWindow and onCloseWindow, and most never do. On iOS, WKWebView's createWebViewWithConfiguration delegate returns nil by default, so target=_blank is silently dropped unless the app implements WKUIDelegate — and when developers do, they often spawn a second WebView whose back and forward history is disconnected from the first. A host-owned runtime ships one window manager that maps popups to in-app tabs or a consistent external handoff, keeps navigation history intact, and never lets a link die silently.

Android: new windows ignored by default

The Android docs are blunt: by default the system ignores requests to open new windows, whether via JavaScript or a target attribute. To honor target=_blank the app must call setSupportMultipleWindows(true) and override onCreateWindow (and onCloseWindow to clean up). Skip the flag and onCreateWindow is never called — the tap produces no navigation, no error, nothing. The practical result across the app ecosystem is a long tail of dead "open in new tab" links that work in a real browser and vanish in the WebView.

iOS: the delegate that returns nil

WKWebView has no built-in tab or window model; when a page asks for a new browsing context it asks the app through WKUIDelegate.createWebViewWithConfiguration. The default implementation returns nil, which means target=_blank and window.open are silently discarded. Developers must set the uiDelegate and implement the method; the common workaround loads the request back into the same view (losing the intended new-tab semantics) or creates a brand-new WKWebView — whose back and forward stack is separate from the original, so the user gets stuck with no way back.

What you lose without a window manager

  • Dead links with no feedback. target=_blank taps do nothing, so users assume the page is broken.
  • Broken history. A second WebView has its own back/forward stack, disconnecting the user from where they came from.
  • No consistent external-link policy. Some links should stay in-app, some should hand off to the system browser; a stock WebView has no rule.
  • Lost session context. A popup that should inherit login or cart state instead starts cold in a separate context.
  • Silent divergence. The same HTML opens a real tab in Safari and does nothing in the embedded view.

The runtime fix

A host-owned runtime ships one window manager below the page. It maps every new-window request to a predictable outcome — an in-app tab that shares the navigation history and session, or a clearly-labeled external handoff to the system browser — and it never swallows a link silently. The page author writes standard target=_blank, and the runtime decides, consistently and per policy, what happens next, on both platforms.

DimensionWebViewHost-owned runtime
New window defaultIgnored (Android) / nil delegate (iOS)Mapped to in-app tab or handoff
Android flagsetSupportMultipleWindows + onCreateWindowHandled by the runtime
iOS pathWKUIDelegate.createWebViewWithConfigurationHandled by the runtime
Navigation historyBroken across a second viewShared, intact
External-link policyNone, per-app guessworkConsistent, configurable
Silent dead linksCommonNever

A browser treats target=_blank as a promise: a new, reachable context. A stock WebView treats it as a request it is free to ignore. A runtime restores the promise — one window manager, intact history, and a link that always goes somewhere the user can see.

Sources