Chrome Custom Tabs let apps open web content in a browser tab that looks like part of the app. They're faster than opening Chrome, more capable than a WebView, and used by millions of apps for in-app browsing. But when App Links are involved, Custom Tabs create a set of behaviors that confuse developers and users alike.
The core issue: when a user taps a link inside a Custom Tab, should Android open the linked app (via App Links) or keep the user in the Custom Tab? The answer depends on the Chrome version, the link verification status, and how the Custom Tab was launched. This guide covers all of it.
For the foundational App Links setup, see the Android App Links complete guide. For the difference between verified and unverified links, see verified vs. unverified App Links.
How Chrome Custom Tabs Work
Chrome Custom Tabs are a feature of Chrome (and other Chromium-based browsers) that lets apps open URLs in a lightweight browser tab. The hosting app can customize the toolbar color, add action buttons, and define animations.
// Launch a Chrome Custom Tab
val customTabsIntent = CustomTabsIntent.Builder()
.setToolbarColor(ContextCompat.getColor(this, R.color.primary))
.setShowTitle(true)
.build()
customTabsIntent.launchUrl(this, Uri.parse("https://example.com/page"))
From the user's perspective, it looks like an in-app browser. From the system's perspective, it's Chrome running in a separate process with a custom UI overlay.
Who Uses Custom Tabs
- Email apps: Gmail, Outlook open links in Custom Tabs
- Social apps: Twitter/X, Reddit, many others
- Chat apps: Slack, Telegram (sometimes)
- Any app that displays web content without building a full WebView
This means your App Links need to work correctly when clicked inside a Custom Tab, because that's how many users will encounter them.
The App Links Behavior in Custom Tabs
Default Behavior (Chrome 90+)
When a user taps a verified App Link inside a Chrome Custom Tab:
- First navigation: The Custom Tab opens the URL in the browser. It does NOT trigger App Links for the initial URL.
- Subsequent navigation: If the user taps a link within the Custom Tab page, and that link is a verified App Link, Chrome checks the Digital Asset Links file and may open the target app.
This means: if your app opens a Custom Tab pointing to https://links.yourapp.com/promo, the system will NOT redirect back to your own app, even if that URL is a verified App Link. This is by design; it prevents a redirect loop.
The Same-App Protection
Chrome specifically prevents a Custom Tab from triggering an App Link back to the app that launched it. This stops the circular navigation pattern:
Your App → Custom Tab → App Link → Your App → Custom Tab → ...
This protection is sensible but causes confusion when:
- Your marketing links and your App Links use the same domain
- You open a web page that contains links back to your own app
- You use a Custom Tab for web-based flows (OAuth, payments) that should redirect back to your app
Cross-App Navigation
App Links to other apps work normally from Custom Tabs. If a user is reading a page in your Custom Tab and taps a link that's a verified App Link for a different app, that app will open.
Configuring Custom Tabs for App Links
Warm-Up the Custom Tabs Service
Pre-connecting to the Custom Tabs service improves performance and ensures the browser is ready to handle navigation correctly.
class MainActivity : AppCompatActivity() {
private var customTabsClient: CustomTabsClient? = null
private var customTabsSession: CustomTabsSession? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Warm up Chrome Custom Tabs
CustomTabsClient.bindCustomTabsService(
this,
"com.android.chrome",
object : CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(
name: ComponentName,
client: CustomTabsClient
) {
customTabsClient = client
client.warmup(0)
customTabsSession = client.newSession(null)
}
override fun onServiceDisconnected(name: ComponentName) {
customTabsClient = null
customTabsSession = null
}
}
)
}
}
Handling Return Navigation
When you use a Custom Tab for a flow that should return to your app (like OAuth), use an intent flag to handle the return:
// For OAuth or payment flows that redirect back to your app
fun launchAuthFlow(authUrl: String) {
val customTabsIntent = CustomTabsIntent.Builder()
.setSession(customTabsSession!!)
.build()
// Add FLAG_ACTIVITY_NO_HISTORY so the Custom Tab doesn't stay in the back stack
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
customTabsIntent.launchUrl(this, Uri.parse(authUrl))
}
For the return redirect, use a custom scheme or an App Link with an intent filter that catches the redirect URL:
<!-- In AndroidManifest.xml -->
<activity
android:name=".AuthCallbackActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="yourapp.com"
android:pathPrefix="/auth/callback" />
</intent-filter>
</activity>
Common Issues and Fixes
Issue 1: App Link Doesn't Trigger from Custom Tab
Symptom: A user taps a verified App Link inside a Custom Tab, but the app doesn't open. The link loads in the browser instead.
Cause: Chrome's same-app protection is active, OR the App Link is not verified.
Fix:
- Verify the link is actually verified:
adb shell pm get-app-links --user cur <package> - If the link targets the same app that launched the Custom Tab, this is expected behavior. Use a redirect through a different domain, or use an explicit intent instead.
# Check verification status
adb shell pm get-app-links --user cur com.example.app
Issue 2: Disambiguation Dialog Appears
Symptom: Instead of opening the app directly, Chrome shows a "Open with" dialog.
Cause: The App Link is not verified (unverified links show a disambiguation dialog) or another app has registered the same domain.
Fix: Ensure your Digital Asset Links file is correctly hosted at https://yourdomain.com/.well-known/assetlinks.json and your app's SHA-256 fingerprint matches.
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example.app",
"sha256_cert_fingerprints": [
"AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"
]
}
}]
Issue 3: Custom Tab Stays Open After App Launch
Symptom: The app opens correctly, but the Custom Tab remains in the recent apps list or back stack.
Fix: Use FLAG_ACTIVITY_NO_HISTORY when launching the Custom Tab, or call finishAffinity() in the Activity that handles the App Link.
Issue 4: Links Don't Work on Non-Chrome Browsers
Symptom: Custom Tabs work fine in Chrome but fail in Samsung Internet, Firefox, or other browsers.
Cause: Custom Tabs behavior varies by browser. Samsung Internet has its own Custom Tabs implementation with different App Link handling.
Fix: Test on the top 3-4 browsers used by your audience. For critical flows, consider using a standard Intent.ACTION_VIEW instead of Custom Tabs:
// Fallback: open in the default browser (supports App Links reliably)
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
Best Practices
Don't use Custom Tabs for your own App Links. If you need to open a URL that should resolve back to your own app, use
Intent.ACTION_VIEWinstead.Use Custom Tabs for third-party web content. They're perfect for opening external links, documentation pages, or web content that doesn't need to redirect back to your app.
Test with multiple browsers. Custom Tab behavior varies across Chrome, Samsung Internet, and other Chromium-based browsers. Test on the browsers your users actually use.
Handle the back button correctly. When a user presses back in a Custom Tab, they should return to your app, not to a blank screen. Set up the activity launch mode correctly.
Pre-warm the connection. Call
CustomTabsClient.warmup()before the user taps a link. This reduces the perceived load time by several hundred milliseconds.For Tolinku deep links, the platform handles browser detection and routing automatically. When a Tolinku link is opened in a Custom Tab, the redirect chain resolves the App Link correctly without triggering the same-app protection issue. See the App Links documentation for setup.
For testing your App Links in Custom Tabs and other contexts, see the testing Android App Links guide.
Get deep linking tips in your inbox
One email per week. No spam.