Deep linking lets external URLs open a specific screen in your app on web, iOS, and Android. One handles deep links by stripping a configured prefix from the incoming URL and matching the remaining path against your file-system routes.
Configure router.linking in your Vite config and register the same scheme in your native app manifest.
vite.config.ts
app.json
With this, opening myapp://settings/profile or https://example.com/app/settings/profile (after universal-link verification) routes to app/settings/profile.tsx.
Each entry in prefixes is an exact URL prefix that gets stripped from incoming deep links before route matching. A scheme is shorthand: 'myapp' expands to 'myapp://' and 'myapp:///' automatically.
Prefixes match at clean boundaries — the URL must be exactly the prefix, or continue with /, ?, or #:
| URL | Prefix | Match | Stripped path |
|---|---|---|---|
myapp://app/thread/1 | myapp://app | yes | thread/1 |
myapp://app?ref=share | myapp://app | yes | ?ref=share |
myapp://app | myapp://app | yes | (empty) |
myapp://application/thread/1 | myapp://app | no | (falls back) |
When multiple prefixes match the same URL, the longest one wins.
On native, schemes registered in your app manifest are picked up automatically and merged with whatever you configure in router.linking. You only need to repeat them in router.linking if you want host-bearing variants (like myapp://app) treated as separate prefixes.
OneLinkingConfig Referencescheme — one or more custom app schemes. Each scheme is expanded internally to ${scheme}:// and ${scheme}:/// URL prefixes. Schemes should also be registered in your native app manifest.prefixes — fully qualified URL prefixes to strip before route matching. Use this for universal links ('https://example.com/app') or host-bearing custom scheme URLs ('myapp://app').filter — optional predicate that gates which incoming URLs are handled. Functions can’t be inlined into the generated entry, so this can only be set programmatically (see below).getLinking HelpergetLinking is a typed identity helper for defining a linking config in TypeScript. It’s useful when you want to share the config across files or pass it programmatically.
filter)To use a filter function, write your own entry that calls createApp({ linking }) directly. The Vite plugin’s router.linking only accepts JSON-serializable fields.
src/entry.tsx
The scheme in app.json lands in Info.plist after running expo prebuild. To verify:
Terminal
Test a deep link against a running simulator:
Terminal
For universal links (https://example.com/app/...), follow Apple’s associated domains setup and add the entitlement.
Add the scheme and any universal-link hosts to AndroidManifest.xml (Expo will generate this from your app.json config). Test with:
Terminal
On web, deep linking is just URL routing — no prefix-stripping is needed because the browser already provides the path via window.location. The configured prefixes and scheme are still accepted for API parity but are inert on web.
useSitemapUse useSitemap to render a list of all routes — handy for debug screens or generating link previews while testing deep links.
useSitemap — list every route in your appEdit this page on GitHub.