To make a WebView open external links in the browser, give it a WebViewClient that overrides shouldOverrideUrlLoading. Return false for links on your own domain so they stay in the app, and send every other link to an Intent.ACTION_VIEW (or a Custom Tab), then return true. Handle mailto:, tel: and other non-web schemes in the same method.
Why every link stays inside your WebView app
A WebView app keeps all links inside because it registered a WebViewClient, and that one line changes the default. The Android WebView guide says that without a client, “Android launches an app that handles URLs”, which is usually the default browser.
Most wrappers set a client so their own pages don’t bounce out to Chrome. The catch is in the same guide: after setWebViewClient, “All links the user taps load in your WebView.” So your privacy policy page, a YouTube link and a payment page all open inside the app.
Outside sites inside your app have no address bar. Users can’t tell where they are.
Where each kind of link should open
Each link type needs its own rule, because WebView treats them differently.
| Link type | Example | Where it should go | What to return |
|---|---|---|---|
| Your own domain | https://yoursite.com/pricing |
Stay in the WebView | false |
| Another website | https://youtube.com/... |
Browser or Custom Tab | true |
| Email, phone, store | mailto:, tel:, market:// |
Intent to the matching app | true |
| New window | target="_blank", window.open() |
Same rule as its URL | depends |
| Third-party sign-in | Google or Facebook login page | Browser or Custom Tab | true |
The last row matters more than it looks. The AdMob WebView guide states that Google Sign-in and Facebook Sign-in aren’t supported in WebView. If your site uses them, see why Google sign-in fails in a WebView app.
How to open external links in the browser with shouldOverrideUrlLoading
Override shouldOverrideUrlLoading(WebView, WebResourceRequest) in your client and compare the link’s host with your own. The WebViewClient reference marks the older String version as deprecated, so use the WebResourceRequest one.
The return value does the work: true stops the WebView from loading the URL, false lets it load.
webView.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
val uri = request.url
val scheme = uri.scheme ?: return false
if ((scheme == "https" || scheme == "http") && uri.host == "yoursite.com") {
return false // own site: let the WebView load it
}
try {
view.context.startActivity(Intent(Intent.ACTION_VIEW, uri))
} catch (e: ActivityNotFoundException) {
// no app on the phone can open this link
}
return true
}
}
To add it:
- Replace
yoursite.comwith your host. Addwww.or subdomains if your site uses them. - Set the client before you call
loadUrlfor the first page. - Keep the
try/catch. A tablet with no phone app can’t opentel:links. - Don’t call
loadUrlinside the method. The guide says not to callloadUrl,reloador similar methods fromshouldOverrideUrlLoading.
Per the reference, the method isn’t called for pages your app opens with loadUrl(), and it isn’t called for POST requests, such as a form submit.
Fixing mailto and tel links: net::ERR_UNKNOWN_URL_SCHEME
A mailto: or tel: link fails inside a WebView because WebView only loads web addresses. The usual symptom is a blank page with net::ERR_UNKNOWN_URL_SCHEME, as in this Stack Overflow question about a mailto link.
The code above already handles it. Any scheme that isn’t your own https host goes to Intent.ACTION_VIEW, so the phone opens the mail app, the dialler or the Play Store. The AdMob guide uses the same pattern for market:// links and notes that WebView doesn’t support custom URL schemes.
What happens to target=“_blank” links
A target="_blank" link in a WebView doesn’t open a new window by default. The WebView guide says requests to open new windows are ignored, whether from JavaScript or from a link’s target attribute.
According to the WebSettings reference, setSupportMultipleWindows defaults to false. In that state the request is treated as a top-level navigation in the same WebView, so the link still goes through your host check.
Turning multiple windows on means writing onCreateWindow yourself. Leave it off unless your site needs pop-ups, such as a payment window that talks back to its opener.
Browser Intent or Custom Tab?
Both send the user out of your WebView; the difference is how far. A plain ACTION_VIEW Intent switches to the user’s browser app. A Custom Tab opens the page over your app.
The Custom Tabs overview explains that Custom Tabs are powered by the user’s preferred browser and share its state, unlike WebViews. It calls them the better choice when your app sends people to URLs outside your domains. The AdMob guide swaps in a CustomTabsIntent for outside links, which needs the androidx.browser library.
I’d start with the plain Intent. It needs no new dependency, and you can switch to Custom Tabs later without touching your host check.
Testing external links before you publish
Test every link type from the table on a real phone, not only in a desktop browser. Check that:
- your own pages, including redirects to
www., stay in the app - a link to another site opens the browser, and Back returns to your app
mailto:andtel:open the right apps- sign-in buttons leave the WebView
The same habit catches other WebView gaps, such as file downloads that silently fail.
A limit of no-code WebView builders
Link handling lives in the app’s native code, so a no-code wrapper decides it for you. Web2Apk’s own guides say it doesn’t guarantee every website works in a WebView, and that external links can behave differently than in Chrome. If your links need custom rules, a no-code builder can’t give you them; a hand-written wrapper or a Trusted Web Activity gives you that control.
Clean link handling also helps at review time. Google Play looks at whether a wrapper adds anything over the website, as covered in why WebView apps get rejected for minimum functionality.
FAQ
Why do all links open inside my WebView app instead of the browser?
Your app set a WebViewClient. Android’s documentation says that once you do, all links the user taps load in your WebView. Override shouldOverrideUrlLoading to send outside links to the browser.
What should shouldOverrideUrlLoading return for external links?
Return true after you start the Intent or Custom Tab, which stops the WebView from loading that URL. Return false for links on your own site so the WebView keeps loading them.
Why do mailto and tel links show net::ERR_UNKNOWN_URL_SCHEME?
WebView only loads web addresses, so it can’t open a mailto:, tel: or market: link itself. Catch those schemes in shouldOverrideUrlLoading and pass them to an ACTION_VIEW Intent, with a try/catch for ActivityNotFoundException.
Why do target=“_blank” links do nothing or replace the page?
WebView ignores new-window requests by default. With multiple-window support off, the link loads as a normal navigation in the same view, so your URL check in shouldOverrideUrlLoading still decides where it goes.
Should external links open in Chrome or a Custom Tab?
A Custom Tab keeps the user inside your app while using their real browser, including its sign-ins. A full browser Intent is simpler and needs no extra library. Both work for leaving your site.