Add deeplinkIntegration for automatic deep link breadcrumbs#5983
Conversation
Introduces a new `deeplinkIntegration` that automatically captures breadcrumbs
whenever the app is opened or foregrounded via a deep link.
- Intercepts cold-start links via `Linking.getInitialURL()`
- Intercepts warm-open links via `Linking.addEventListener('url', ...)`
- Breadcrumbs use `category: 'deeplink'` and `type: 'navigation'`
- Respects `sendDefaultPii`: when false, query strings are stripped and
numeric / UUID / long-hex path segments are replaced with `<id>`
- Compatible with both Expo Router and plain React Navigation deep linking
(uses the standard RN `Linking` API, no framework-specific dependencies)
- Gracefully skips setup when Linking is unavailable (e.g. web)
Closes #5424
Semver Impact of This PR⚪ None (no version bump detected) 📋 Changelog PreviewThis is how your changes will appear in the changelog.
Plus 8 more 🤖 This preview updates automatically when you update the PR. |
|
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 560ceb9. Configure here.
- Strip URL fragments (#) in addition to query strings when sendDefaultPii is off, preventing PII leaks via fragment identifiers - Store the Linking event listener subscription and remove it on client close to prevent resource leaks and duplicate breadcrumbs on hot reload - Cache getBreadcrumbUrl result to avoid redundant getClient lookups and regex sanitization - Extract RNLinking and LinkingSubscription interfaces for cleaner types - Add tests for fragment stripping, combined query+fragment stripping, and subscription cleanup on client close
…linkIntegration Replace the duplicated stripQueryAndFragment/sanitizeUrl logic with the existing exported sanitizeUrl from tracing/utils.ts, which already strips query strings and fragments. The deeplink-specific ID replacement regex is kept in a new sanitizeDeepLinkUrl wrapper.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1a19735. Configure here.
antonis
left a comment
There was a problem hiding this comment.
Other that the two issues flagged by the bots and the lint failure LGTM!
…kIntegration - Split URL into authority and path before applying ID-replacement regex so hostnames that resemble hex strings (e.g. myapp://deadbeef/...) are not incorrectly replaced with <id> - Move subscription to the factory closure and remove it on repeated setup calls to prevent duplicate listeners when Sentry.init() is called more than once - Add tests for hostname preservation and repeated setup cleanup
…compiled output
The local variable 'Linking' from tryGetLinking() collided with the
destructured 'Linking' from require('react-native') in the compiled JS,
causing a 'Duplicate declaration' build error. Renamed to 'linking'.
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5c1e987+dirty | 1204.30 ms | 1222.15 ms | 17.85 ms |
| 2c735cc+dirty | 1229.67 ms | 1221.50 ms | -8.17 ms |
| df5d108+dirty | 1225.90 ms | 1220.14 ms | -5.76 ms |
| 4953e94+dirty | 1212.06 ms | 1214.83 ms | 2.77 ms |
| 3ce5254+dirty | 1219.93 ms | 1221.90 ms | 1.96 ms |
| 3817909+dirty | 1183.90 ms | 1187.50 ms | 3.60 ms |
| a50b33d+dirty | 1197.74 ms | 1197.17 ms | -0.57 ms |
| 04207c4+dirty | 1191.27 ms | 1189.78 ms | -1.48 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5c1e987+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 2c735cc+dirty | 3.38 MiB | 4.74 MiB | 1.35 MiB |
| df5d108+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 4953e94+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 3ce5254+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 3817909+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| a50b33d+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 04207c4+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
📲 Install BuildsAndroid
|
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5c1e987+dirty | 1208.43 ms | 1220.72 ms | 12.29 ms |
| 2c735cc+dirty | 1223.33 ms | 1224.38 ms | 1.04 ms |
| df5d108+dirty | 1207.34 ms | 1210.50 ms | 3.16 ms |
| 4953e94+dirty | 1217.41 ms | 1223.53 ms | 6.12 ms |
| 3ce5254+dirty | 1217.70 ms | 1224.69 ms | 6.99 ms |
| 3817909+dirty | 1210.76 ms | 1215.64 ms | 4.89 ms |
| a50b33d+dirty | 1207.11 ms | 1212.10 ms | 5.00 ms |
| 04207c4+dirty | 1228.55 ms | 1226.04 ms | -2.51 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 5c1e987+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 2c735cc+dirty | 3.38 MiB | 4.74 MiB | 1.35 MiB |
| df5d108+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 4953e94+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 3ce5254+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
| 3817909+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| a50b33d+dirty | 3.38 MiB | 4.73 MiB | 1.35 MiB |
| 04207c4+dirty | 3.38 MiB | 4.76 MiB | 1.38 MiB |
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 3ce5254+dirty | 373.90 ms | 427.84 ms | 53.94 ms |
| 4953e94+dirty | 398.80 ms | 431.81 ms | 33.01 ms |
| 2c735cc+dirty | 435.20 ms | 459.48 ms | 24.28 ms |
| 3817909+dirty | 357.52 ms | 391.52 ms | 34.00 ms |
| df5d108+dirty | 434.82 ms | 447.39 ms | 12.57 ms |
| 04207c4+dirty | 395.40 ms | 456.55 ms | 61.15 ms |
| a50b33d+dirty | 353.21 ms | 398.48 ms | 45.27 ms |
| 5c1e987+dirty | 444.71 ms | 475.13 ms | 30.42 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 3ce5254+dirty | 43.94 MiB | 48.98 MiB | 5.04 MiB |
| 4953e94+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| 2c735cc+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| 3817909+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| df5d108+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| 04207c4+dirty | 43.94 MiB | 48.98 MiB | 5.04 MiB |
| a50b33d+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
| 5c1e987+dirty | 43.94 MiB | 48.94 MiB | 5.00 MiB |
…lay-stubs.jar - Rename destructured Linking to rnLinking inside tryGetLinking to avoid Babel/Metro duplicate declaration errors in compiled output - Fix getInitialURL call chain formatting per lint - Revert unrelated replay-stubs.jar binary change
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 3ce5254+dirty | 410.57 ms | 448.48 ms | 37.91 ms |
| 4953e94+dirty | 442.02 ms | 456.52 ms | 14.50 ms |
| 2c735cc+dirty | 414.09 ms | 438.47 ms | 24.38 ms |
| 3817909+dirty | 406.67 ms | 416.58 ms | 9.91 ms |
| df5d108+dirty | 527.06 ms | 603.58 ms | 76.52 ms |
| 04207c4+dirty | 459.19 ms | 518.54 ms | 59.35 ms |
| a50b33d+dirty | 500.81 ms | 532.11 ms | 31.30 ms |
| 5c1e987+dirty | 423.52 ms | 471.64 ms | 48.12 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 3ce5254+dirty | 43.75 MiB | 48.12 MiB | 4.37 MiB |
| 4953e94+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| 2c735cc+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| 3817909+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| df5d108+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| 04207c4+dirty | 43.75 MiB | 48.12 MiB | 4.37 MiB |
| a50b33d+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| 5c1e987+dirty | 43.75 MiB | 48.08 MiB | 4.33 MiB |
| const { Linking: rnLinking } = require('react-native') as { Linking: RNLinking }; | ||
| return rnLinking ?? null; |
There was a problem hiding this comment.
Managed to overcome the persisting build issue locally with this change 🤞
| const { Linking: rnLinking } = require('react-native') as { Linking: RNLinking }; | |
| return rnLinking ?? null; | |
| return (require('react-native') as { Linking: RNLinking }).Linking ?? null; |
antonis
left a comment
There was a problem hiding this comment.
Left a suggestion that would hopefully fix the build issue (without creating other issues) 🤞
Other than that the PR looks good overall
…icate declaration Replace destructured import with direct property access on require result as suggested by @antonis to fix the persisting Babel/Metro build error.
antonis
left a comment
There was a problem hiding this comment.
LGTM 🚀
Let's wait for the non flaky tests to go 🟢 before merging

📢 Type of change
📜 Description
Introduces a new
deeplinkIntegrationthat automatically captures breadcrumbs whenever the app is opened or foregrounded via a deep link.Closes #5424
📝 Checklist
sendDefaultPIIis enabled🔮 Next steps