{"id":1539,"date":"2026-06-21T17:00:00","date_gmt":"2026-06-21T22:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=1539"},"modified":"2026-03-07T03:49:33","modified_gmt":"2026-03-07T08:49:33","slug":"deep-linking-instant-apps","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/deep-linking-instant-apps\/","title":{"rendered":"Deep Linking and Android Instant Apps"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/developer.android.com\/topic\/google-play-instant\" rel=\"nofollow noopener\" target=\"_blank\">Android Instant Apps<\/a> (now called Google Play Instant) let users run your app from a URL without installing it. The user taps a link, a lightweight version of your app loads in seconds, and they interact with it immediately. If they like it, they can install the full app. The deep link URL that triggered the instant experience carries over.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers how to build Instant App experiences powered by deep links. For the iOS equivalent, see <a href=\"https:\/\/tolinku.com\/blog\/deep-linking-app-clips\/\">deep linking and App Clips<\/a>. For Android App Links setup, see <a href=\"https:\/\/tolinku.com\/blog\/android-instant-apps-deep-links\/\">Android Instant Apps and deep linking<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How Instant Apps Work with Deep Links<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The URL-to-Experience Flow<\/h3>\n\n\n\n<pre><code>User taps link: https:\/\/yourapp.com\/products\/running-shoes\n  \u2192 Android checks: is the full app installed?\n    \u2192 Yes: open the full app via App Links\n    \u2192 No: is Google Play Instant enabled for this URL?\n      \u2192 Yes: load the Instant App module (~15MB)\n        \u2192 Instant App opens to the product page\n      \u2192 No: open the URL in Chrome\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The same URL handles all three cases. App Links verification is required for all of them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Instant App Module Structure<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">An Instant App is split into feature modules. Each module handles specific URLs:<\/p>\n\n\n\n<pre><code>app\/\n  \u251c\u2500\u2500 base\/                    (shared code, always loaded)\n  \u251c\u2500\u2500 feature-products\/        (handles \/products\/* URLs, ~5MB)\n  \u251c\u2500\u2500 feature-checkout\/        (handles \/checkout\/* URLs, ~4MB)\n  \u2514\u2500\u2500 feature-account\/         (handles \/account\/* URLs, ~3MB)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">When a user taps a <code>\/products\/running-shoes<\/code> link, only the <code>base<\/code> and <code>feature-products<\/code> modules load. The checkout module loads when the user navigates to checkout.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up Instant App Deep Links<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">App Links Verification<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Instant Apps require verified App Links. The <code>assetlinks.json<\/code> file must include the Instant App package:<\/p>\n\n\n\n<pre><code class=\"language-json\">[{\n  &quot;relation&quot;: [&quot;delegate_permission\/common.handle_all_urls&quot;],\n  &quot;target&quot;: {\n    &quot;namespace&quot;: &quot;android_app&quot;,\n    &quot;package_name&quot;: &quot;com.yourapp&quot;,\n    &quot;sha256_cert_fingerprints&quot;: [\n      &quot;YOUR_RELEASE_FINGERPRINT&quot;,\n      &quot;YOUR_INSTANT_APP_FINGERPRINT&quot;\n    ]\n  }\n}]\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Manifest Configuration<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Each feature module declares its URL patterns in the manifest:<\/p>\n\n\n\n<pre><code class=\"language-xml\">&lt;!-- feature-products\/src\/main\/AndroidManifest.xml --&gt;\n&lt;manifest xmlns:android=&quot;http:\/\/schemas.android.com\/apk\/res\/android&quot;\n    xmlns:dist=&quot;http:\/\/schemas.android.com\/apk\/distribution&quot;\n    package=&quot;com.yourapp.feature.products&quot;&gt;\n\n    &lt;dist:module\n        dist:instant=&quot;true&quot;\n        dist:title=&quot;Products&quot;&gt;\n        &lt;dist:delivery&gt;\n            &lt;dist:install-time \/&gt;\n        &lt;\/dist:delivery&gt;\n    &lt;\/dist:module&gt;\n\n    &lt;application&gt;\n        &lt;activity\n            android:name=&quot;.ProductActivity&quot;\n            android:exported=&quot;true&quot;&gt;\n            &lt;intent-filter android:autoVerify=&quot;true&quot;\n                android:order=&quot;1&quot;&gt;\n                &lt;action android:name=&quot;android.intent.action.VIEW&quot; \/&gt;\n                &lt;category android:name=&quot;android.intent.category.DEFAULT&quot; \/&gt;\n                &lt;category android:name=&quot;android.intent.category.BROWSABLE&quot; \/&gt;\n                &lt;data android:scheme=&quot;https&quot;\n                      android:host=&quot;yourapp.com&quot;\n                      android:pathPrefix=&quot;\/products\/&quot; \/&gt;\n            &lt;\/intent-filter&gt;\n\n            &lt;meta-data\n                android:name=&quot;default-url&quot;\n                android:value=&quot;https:\/\/yourapp.com\/products&quot; \/&gt;\n        &lt;\/activity&gt;\n    &lt;\/application&gt;\n&lt;\/manifest&gt;\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Handling the Deep Link in the Activity<\/h3>\n\n\n\n<pre><code class=\"language-kotlin\">class ProductActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        val uri = intent.data ?: run {\n            showDefaultProducts()\n            return\n        }\n\n        val productSlug = uri.lastPathSegment\n        val variant = uri.getQueryParameter(&quot;variant&quot;)\n        val campaign = uri.getQueryParameter(&quot;utm_campaign&quot;)\n\n        viewModel.loadProduct(productSlug, variant)\n\n        \/\/ Track that this came from an instant app experience\n        if (isInstantApp()) {\n            analytics.trackInstantAppView(productSlug, campaign)\n        }\n    }\n\n    private fun isInstantApp(): Boolean {\n        return packageManager.isInstantApp\n    }\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Transitioning from Instant to Installed<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Install Prompt<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When the user wants to do something the Instant App cannot handle (e.g., create an account, access offline features), prompt them to install:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">fun promptInstall() {\n    val intent = Intent(Intent.ACTION_VIEW).apply {\n        data = Uri.parse(&quot;https:\/\/play.google.com\/store\/apps\/details?id=com.yourapp&quot;)\n        setPackage(&quot;com.android.vending&quot;)\n        putExtra(&quot;callerId&quot;, packageName)\n        putExtra(&quot;referrer&quot;, &quot;utm_source=instant_app&amp;utm_medium=install_prompt&quot;)\n    }\n    startActivity(intent)\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Preserving State Through Install<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use the <a href=\"https:\/\/developer.android.com\/reference\/com\/google\/android\/gms\/instantapps\/PackageManagerCompat\" rel=\"nofollow noopener\" target=\"_blank\">Cookie API<\/a> to transfer data from the Instant App to the installed app:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">import com.google.android.gms.instantapps.PackageManagerCompat\n\n\/\/ Instant App: save state before install\nfun saveStateForInstall(userId: String, cartItems: List&lt;CartItem&gt;) {\n    val cookie = JSONObject().apply {\n        put(&quot;userId&quot;, userId)\n        put(&quot;cart&quot;, JSONArray(cartItems.map { it.toJSON() }))\n        put(&quot;lastViewedProduct&quot;, currentProductSlug)\n    }.toString().toByteArray()\n\n    PackageManagerCompat.setInstantAppCookie(this, cookie)\n}\n\n\/\/ Installed app: restore state after install\nfun restoreFromInstantApp() {\n    val cookie = PackageManagerCompat.getInstantAppCookie(this)\n    if (cookie.isNotEmpty()) {\n        val state = JSONObject(String(cookie))\n        val userId = state.optString(&quot;userId&quot;)\n        val lastProduct = state.optString(&quot;lastViewedProduct&quot;)\n\n        if (userId.isNotEmpty()) {\n            authManager.restoreSession(userId)\n        }\n        if (lastProduct.isNotEmpty()) {\n            navigateToProduct(lastProduct)\n        }\n\n        \/\/ Clear the cookie after restoring\n        PackageManagerCompat.setInstantAppCookie(this, ByteArray(0))\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Deep Link Continuity<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">After installation, the same URLs that triggered the Instant App now open the installed app. No URL changes needed. The App Links verification covers both instant and installed versions.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Size Optimization<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">The 15MB Limit<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Each Instant App module must be under 15MB (including the base module). Strategies to stay within the limit:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Move large assets to a CDN.<\/strong> Download images, 3D models, and videos on demand.<\/li>\n<li><strong>Use dynamic feature modules.<\/strong> Split features so only the needed code loads.<\/li>\n<li><strong>Minimize dependencies.<\/strong> Each library adds to the module size.<\/li>\n<li><strong>Use WebP images.<\/strong> Smaller than PNG\/JPEG for the same quality.<\/li>\n<li><strong>Enable ProGuard\/R8.<\/strong> Remove unused code and resources.<\/li>\n<\/ol>\n\n\n\n<pre><code class=\"language-groovy\">\/\/ build.gradle - enable code shrinking\nandroid {\n    buildTypes {\n        release {\n            minifyEnabled true\n            shrinkResources true\n            proguardFiles getDefaultProguardFile(&#39;proguard-android-optimize.txt&#39;)\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Feature Module Boundaries<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Design your modules around URL patterns:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Module<\/th>\n<th>URLs<\/th>\n<th>Size Budget<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>base<\/td>\n<td>(always loaded)<\/td>\n<td>~5MB<\/td>\n<\/tr>\n<tr>\n<td>products<\/td>\n<td>\/products\/*<\/td>\n<td>~4MB<\/td>\n<\/tr>\n<tr>\n<td>search<\/td>\n<td>\/search*<\/td>\n<td>~3MB<\/td>\n<\/tr>\n<tr>\n<td>checkout<\/td>\n<td>\/checkout\/*<\/td>\n<td>~3MB<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Instant Apps and Attribution<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">UTM Parameters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">UTM parameters in the deep link URL pass through to the Instant App:<\/p>\n\n\n\n<pre><code>https:\/\/yourapp.com\/products\/shoes?utm_source=email&amp;utm_campaign=summer&amp;utm_medium=link\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Track these in the Instant App to measure which channels drive instant app experiences.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Install Referrer<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When the user installs from the Instant App, the install referrer can carry context:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">\/\/ Include referrer when prompting install\nval referrer = &quot;utm_source=instant_app&amp;utm_content=${currentProductSlug}&quot;\nval playStoreUri = Uri.parse(\n    &quot;https:\/\/play.google.com\/store\/apps\/details?id=com.yourapp&amp;referrer=$referrer&quot;\n)\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Tolinku and Instant Apps<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/tolinku.com\/features\/deep-linking\">Tolinku<\/a> handles App Links verification for both installed and Instant App versions. Configure your routes in the <a href=\"https:\/\/tolinku.com\/docs\/concepts\/deep-linking\/\">Tolinku dashboard<\/a>, and the same <code>assetlinks.json<\/code> serves both app modes. The web fallback page shows for users on non-Android devices or when the Instant App module is not available.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For more on deep linking trends, see <a href=\"https:\/\/tolinku.com\/blog\/future-mobile-deep-linking\/\">the future of mobile deep linking<\/a>. For App Clips (the iOS equivalent), see <a href=\"https:\/\/tolinku.com\/blog\/deep-linking-app-clips\/\">deep linking and App Clips<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Leverage deep linking with Android Instant Apps. Let users try your app via deep links before committing to a full install.<\/p>\n","protected":false},"author":2,"featured_media":1538,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Deep Linking and Android Instant Apps","rank_math_description":"Leverage deep linking with Android Instant Apps. Let users try your app via deep links before committing to a full install.","rank_math_focus_keyword":"deep linking Instant Apps","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-deep-linking-instant-apps.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-deep-linking-instant-apps.png","footnotes":""},"categories":[11],"tags":[25,23,20,111,67,69,403,33],"class_list":["post-1539","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-deep-linking","tag-android","tag-app-links","tag-deep-linking","tag-google-play","tag-instant-apps","tag-mobile-development","tag-try-before-install","tag-user-experience"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1539","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=1539"}],"version-history":[{"count":3,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1539\/revisions"}],"predecessor-version":[{"id":2627,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/1539\/revisions\/2627"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/1538"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=1539"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=1539"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=1539"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}