◉RORK LABJP
●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…●GPT-6.1 SOL — Added to Rork's model menu (09-29) with Low, Medium and High effort; included on Pro and Max●SONNET 5.5 — Claude Sonnet 5.5 is selectable: strong at UI design and faster than Sonnet 5 (09-28)●SONNET 4.5 — Retires on the Claude API on 11/30, 27 days left. If your app calls it, check the migration target●SDK 58 — Moves to React Native 0.88. R8 is now on by default in Android release builds, and iOS prebuild generates a SceneDelegate●OBSERVE — EAS Observe now records native crashes too (SDK 57.0.21 or later, 10/07)●NEW — My App Open Ad Only Showed When the App…
Articles/Dev Tools
⬡ Dev Tools/2026-06-16Advanced

Landing Users on the Right Screen Right After Install — Deferred Deep Links for Rork Apps

When someone follows a campaign link and installs through the store, the 'where did they come from' context is gone by launch time. Here is how to implement deferred deep linking in a Rork-built app without any third-party SDK.

Rork611Deep LinkExpo234Universal LinksOnboarding9Attribution5

✦ Premium Article

Someone taps "Open in app" on a page showcasing a specific wallpaper theme, installs, launches — and lands on the plain home screen. They came for that theme, and now they have to hunt for it from scratch. When I started running campaigns as a solo developer, this was exactly what I was leaking.

The cause is simple. When a user taps a link but the app isn't installed, they get bounced to the App Store / Google Play first. The app installed and launched from the store no longer receives any "which link did they come from" context. The link's query parameters disappear the moment the store steps in between.

Deferred deep linking bridges that break. An SDK like Branch or AppsFlyer solves it in one shot, but those SDKs hoard a lot of personal data for attribution and carry a monthly cost. For both privacy and cost reasons, I chose to build a lightweight version myself. Here is that design, written for a Rork (Expo / React Native) app.

Start by sorting out "there are three paths"

Recovering a deferred deep link has three paths with different reliability. Conflating them makes the implementation needlessly complex.

  1. Already-installed users (Universal Links / App Links work directly — most reliable)
  2. iOS installs from the same browser (via clipboard — conditionally reliable)
  3. Everything else (server-side fingerprint matching — probabilistic)

Path 1 isn't deferred at all; it's a normal deep link, so I won't cover it here. The problem is paths 2 and 3. To carry information across the "break" that installation creates, you have to stash it temporarily outside the app — on a server or in the clipboard.

In my setup, iOS uses path 2 primarily with path 3 as a fallback. Android gets its path officially from Google Play's Install Referrer API, so I use that. The fact that the mechanisms differ entirely by platform is worth internalizing up front.

Stash a "fingerprint" on your server from the landing page

Without a third-party SDK, you record the moment-of-tap context on your own backend — only loose, non-identifying features (a fingerprint).

// landing.ts — call this the moment the landing page is tapped
async function recordClick(targetPath: string) {
  const fp = {
    target: targetPath,                  // the screen to land on (e.g. /theme/aurora)
    platform: /iphone|ipad/i.test(navigator.userAgent) ? "ios" : "other",
    lang: navigator.language,
    tzOffset: new Date().getTimezoneOffset(),
    screen: `${screen.width}x${screen.height}`,
    ts: Date.now(),
  };
  // On iOS, also keep a copy in the clipboard (the primary path-2 mechanism)
  try {
    await navigator.clipboard.writeText(`rork-dl:${targetPath}`);
  } catch {
    // If clipboard is unavailable, defer to path-3 server matching
  }
  await fetch("https://api.example.com/dl/click", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(fp),
  });
  location.href = "https://apps.apple.com/app/idXXXXXXXX"; // off to the store
}

Writing to the clipboard is the highest-confidence clue on iOS — it's roughly equivalent to "a first launch coming from the same Safari session." But if the user overwrites the clipboard with another action, it's gone, so keep it as the primary mechanism only and run server matching alongside as insurance.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Why link context vanishes through the store, and how to choose among three recovery paths
✦Recovering the destination with your own backend + fingerprint matching, no third-party SDK
✦The matching window and confidence threshold that prevent wrong-screen landings
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ Dev Tools2026-10-30
It runs in development but the iOS Release build dies on launch — checking whether a missing scheme is the cause
In an expo-router app, an app.json with no scheme can crash only the iOS Release build at launch. Why Debug and Android survive, the two ways to fix it, how to think about leaving the scheme out on purpose, and a pre-submission check.
⬡ Dev Tools2026-10-19
One Missing Tutorial Image Was Enough to Stop the Whole App from Launching. Moving Nice-to-Have Features Off the Required Startup Path
On some devices the app never launched at all. The cause was a design where a missing tutorial image failed the whole screen build. A small wrapper that takes optional features off the startup path, and a pre-release check.
⬡ Dev Tools2026-10-15
When EXPO_PUBLIC_ Values Read Fine in Dev but Come Back undefined in Production: Telling the Spellings Apart
The API URL reads fine while developing and turns undefined once the app is built. The cause was how process.env was written. I lined up babel-preset-expo's output for dev and production and tabulated which spellings get replaced and which are left behind.