@userflow-tech/react-native 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +160 -126
  2. package/docs/anchoring.md +3 -4
  3. package/docs/troubleshooting.md +27 -24
  4. package/expo-plugin/hostDependencyWarnings.d.ts +5 -2
  5. package/expo-plugin/hostDependencyWarnings.d.ts.map +1 -1
  6. package/expo-plugin/hostDependencyWarnings.js +12 -2
  7. package/expo-plugin/hostDependencyWarnings.js.map +1 -1
  8. package/lib/commonjs/authoring/capturePickerScreenshot.js +9 -1
  9. package/lib/commonjs/authoring/capturePickerScreenshot.js.map +1 -1
  10. package/lib/commonjs/evalJs/getWebView.js +5 -13
  11. package/lib/commonjs/evalJs/getWebView.js.map +1 -1
  12. package/lib/commonjs/hostAppVersion.js +6 -1
  13. package/lib/commonjs/hostAppVersion.js.map +1 -1
  14. package/lib/commonjs/installCheck.js +61 -29
  15. package/lib/commonjs/installCheck.js.map +1 -1
  16. package/lib/commonjs/nativeModules.js +44 -0
  17. package/lib/commonjs/nativeModules.js.map +1 -0
  18. package/lib/commonjs/network.js +20 -7
  19. package/lib/commonjs/network.js.map +1 -1
  20. package/lib/commonjs/pairing/tokenStorage.js +17 -4
  21. package/lib/commonjs/pairing/tokenStorage.js.map +1 -1
  22. package/lib/commonjs/surfaces/BannerSurface.js +3 -1
  23. package/lib/commonjs/surfaces/BannerSurface.js.map +1 -1
  24. package/lib/commonjs/surfaces/safeArea.js +6 -0
  25. package/lib/commonjs/surfaces/safeArea.js.map +1 -1
  26. package/lib/commonjs/version.js +1 -1
  27. package/lib/module/authoring/capturePickerScreenshot.js +9 -1
  28. package/lib/module/authoring/capturePickerScreenshot.js.map +1 -1
  29. package/lib/module/evalJs/getWebView.js +5 -13
  30. package/lib/module/evalJs/getWebView.js.map +1 -1
  31. package/lib/module/hostAppVersion.js +6 -1
  32. package/lib/module/hostAppVersion.js.map +1 -1
  33. package/lib/module/installCheck.js +56 -26
  34. package/lib/module/installCheck.js.map +1 -1
  35. package/lib/module/nativeModules.js +40 -0
  36. package/lib/module/nativeModules.js.map +1 -0
  37. package/lib/module/network.js +20 -7
  38. package/lib/module/network.js.map +1 -1
  39. package/lib/module/pairing/tokenStorage.js +17 -4
  40. package/lib/module/pairing/tokenStorage.js.map +1 -1
  41. package/lib/module/surfaces/BannerSurface.js +3 -1
  42. package/lib/module/surfaces/BannerSurface.js.map +1 -1
  43. package/lib/module/surfaces/safeArea.js +5 -0
  44. package/lib/module/surfaces/safeArea.js.map +1 -1
  45. package/lib/module/version.js +1 -1
  46. package/lib/typescript/commonjs/src/authoring/capturePickerScreenshot.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/src/evalJs/getWebView.d.ts +2 -1
  48. package/lib/typescript/commonjs/src/evalJs/getWebView.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/src/hostAppVersion.d.ts +1 -1
  50. package/lib/typescript/commonjs/src/hostAppVersion.d.ts.map +1 -1
  51. package/lib/typescript/commonjs/src/installCheck.d.ts +18 -3
  52. package/lib/typescript/commonjs/src/installCheck.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/src/nativeModules.d.ts +13 -0
  54. package/lib/typescript/commonjs/src/nativeModules.d.ts.map +1 -0
  55. package/lib/typescript/commonjs/src/network.d.ts.map +1 -1
  56. package/lib/typescript/commonjs/src/pairing/tokenStorage.d.ts.map +1 -1
  57. package/lib/typescript/commonjs/src/surfaces/BannerSurface.d.ts.map +1 -1
  58. package/lib/typescript/commonjs/src/surfaces/safeArea.d.ts +2 -0
  59. package/lib/typescript/commonjs/src/surfaces/safeArea.d.ts.map +1 -1
  60. package/lib/typescript/commonjs/src/version.d.ts +1 -1
  61. package/lib/typescript/module/src/authoring/capturePickerScreenshot.d.ts.map +1 -1
  62. package/lib/typescript/module/src/evalJs/getWebView.d.ts +2 -1
  63. package/lib/typescript/module/src/evalJs/getWebView.d.ts.map +1 -1
  64. package/lib/typescript/module/src/hostAppVersion.d.ts +1 -1
  65. package/lib/typescript/module/src/hostAppVersion.d.ts.map +1 -1
  66. package/lib/typescript/module/src/installCheck.d.ts +18 -3
  67. package/lib/typescript/module/src/installCheck.d.ts.map +1 -1
  68. package/lib/typescript/module/src/nativeModules.d.ts +13 -0
  69. package/lib/typescript/module/src/nativeModules.d.ts.map +1 -0
  70. package/lib/typescript/module/src/network.d.ts.map +1 -1
  71. package/lib/typescript/module/src/pairing/tokenStorage.d.ts.map +1 -1
  72. package/lib/typescript/module/src/surfaces/BannerSurface.d.ts.map +1 -1
  73. package/lib/typescript/module/src/surfaces/safeArea.d.ts +2 -0
  74. package/lib/typescript/module/src/surfaces/safeArea.d.ts.map +1 -1
  75. package/lib/typescript/module/src/version.d.ts +1 -1
  76. package/package.json +13 -8
  77. package/src/authoring/capturePickerScreenshot.ts +10 -1
  78. package/src/evalJs/getWebView.ts +6 -13
  79. package/src/hostAppVersion.ts +7 -1
  80. package/src/installCheck.ts +67 -33
  81. package/src/nativeModules.ts +48 -0
  82. package/src/network.ts +27 -11
  83. package/src/pairing/tokenStorage.ts +23 -7
  84. package/src/surfaces/BannerSurface.tsx +7 -5
  85. package/src/surfaces/safeArea.ts +5 -0
  86. package/src/version.ts +1 -1
  87. package/vendor/core/package.json +1 -1
  88. package/vendor/protocol/package.json +1 -1
  89. package/docs/theme-mapping.md +0 -130
  90. package/docs/webview-custom-banner-poc.md +0 -64
package/README.md CHANGED
@@ -1,40 +1,53 @@
1
1
  # `@userflow-tech/react-native`
2
2
 
3
3
  Official Userflow SDK for React Native and Expo. Deliver guided flows, tooltips,
4
- and modals authored in the Userflow dashboard — without shipping a new app
5
- release for every content change.
4
+ modals, and banners authored in the Userflow dashboard — without shipping a new
5
+ app release for every content change.
6
6
 
7
- > Early development. APIs may change. Packages publish to a private npm org
8
- > (`publishConfig.access: restricted`).
7
+ > Early development. APIs may change.
9
8
 
10
9
  ## Requirements
11
10
 
12
- Declared minimums: React `>=18.2.0`, React Native `>=0.74.0`. This package's
13
- test suite runs on React Native 0.81 / React 19.1. The TinyTime sample app in
14
- this repo runs on Expo SDK 57 / React Native 0.86. Expo Go is not supported.
15
-
16
- Install AsyncStorage, WebView, and (on bare React Native) device-info in the
17
- **app**, even though this package also depends on them. Expo SDK 54 and later
18
- autolink transitive native modules. Bare React Native, and Expo SDK 53 and
19
- older, only autolink packages listed in the app's own `package.json`. A direct
20
- install also picks a version that matches the app's React Native.
21
-
22
- | Package | Required? | Why | If missing |
23
- | ------------------------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
24
- | `react`, `react-native` | Yes | Host runtime. | The app cannot load the SDK. |
25
- | `@react-native-async-storage/async-storage` | Yes, install in the app | Anonymous user id and the pairing device id. | `identifyAnonymous()` throws. Pairing keeps a device id in memory for the session. A dev-only warning is logged from `Userflow.init`. |
26
- | `react-native-device-info` | Yes on bare React Native | Host app `app_version` / `app_build`. Expo reads `expo-application` / `expo-constants` instead. | Those attributes are omitted. A dev-only warning is logged on bare React Native. |
27
- | `react-native-view-shot` `>=5.0.0` | Yes (peer) | Builder element-selection screenshots. | The picker has no screenshot. A dev-only warning is logged from `Userflow.init`. |
28
- | `react-native-webview` `>=13.8.0` | Yes, install in the app | Evaluate JavaScript and WebView banners. | Those surfaces do not run. A dev-only warning is logged from `Userflow.init`. |
29
- | `react-native-safe-area-context` | Optional | Insets for modal and tooltip surfaces. | One dev warning. Flow cards use the device's initial safe-area insets, or zero when those are unavailable. |
30
- | `@react-navigation/native` | Optional | Screen tracking and NAVIGATE routing (React Navigation). | Report screens yourself with `updateScreen` / `useUserflowScreen`. |
31
- | `expo-router` | Optional | Screen tracking and NAVIGATE routing (Expo Router). | Report screens yourself with `updateScreen` / `useUserflowScreen`. |
32
- | `@react-native-community/netinfo` | Optional | Instant offline→online reconnect. | Recovery still runs on the heartbeat and when the app returns to the foreground. |
33
- | `expo-secure-store` | Optional | Persist the Builder pairing JWT in Keychain/Keystore. | The pairing token stays in memory and is lost when the process ends. |
11
+ Declared minimums: React `>=18.2.0`, React Native `>=0.76.0`. React Native
12
+ 0.74 and 0.75 cannot install this SDK: `react-native-view-shot` 5.x, which
13
+ Builder element selection needs, requires React Native 0.76 or later. The SDK
14
+ is tested on React Native 0.81 / React 19.1 and on Expo SDK 57 / React Native
15
+ 0.86. Expo Go is not supported.
16
+
17
+ `@react-native-async-storage/async-storage` is the only native module this
18
+ package depends on, so npm installs it. `react-native-webview` and
19
+ `react-native-view-shot` are required peers. npm 7+ and pnpm install required
20
+ peers. Yarn 1 and `npm install --legacy-peer-deps` do not, which is why the
21
+ install commands list WebView and view-shot explicitly.
22
+ `react-native-device-info` is an optional peer for bare React Native.
23
+
24
+ Install those packages in the **app** as well. Expo SDK 54 and later autolink
25
+ native modules found anywhere in the tree. Bare React Native, and Expo SDK 53
26
+ and older, only autolink packages listed in the app's own `package.json`. A
27
+ direct install also picks a version that matches the app's React Native, inside
28
+ the ranges below.
29
+
30
+ | Package | Kind | Why | If missing |
31
+ | ------------------------------------------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
32
+ | `react`, `react-native` `>=0.76.0` | Required peers | Host runtime. | The app cannot load the SDK. React Native older than 0.76 fails install (`ERESOLVE`) because view-shot 5.x requires it. |
33
+ | `@react-native-async-storage/async-storage` `>=1.23 <3` | Dependency | Anonymous user id and the pairing device id. | `identifyAnonymous()` throws. Pairing keeps a device id in memory for the session. A dev-only warning is logged from `Userflow.init`. |
34
+ | `react-native-device-info` `>=11 <16` | Optional peer, bare React Native | Host app `app_version` / `app_build`. Expo reads `expo-application` / `expo-constants` instead. | Those attributes are omitted. A dev-only warning is logged on bare React Native. |
35
+ | `react-native-view-shot` `^5.0.0` | Required peer | Builder element-selection screenshots. | The picker has no screenshot. A dev-only warning is logged from `Userflow.init`. Version 6.x is outside the tested range and requires React Native 0.80 or later. |
36
+ | `react-native-webview` `>=13.8.0 <14` | Required peer | Evaluate JavaScript and WebView banners. | Those surfaces do not run. A dev-only warning is logged from `Userflow.init`. |
37
+ | `react-native-safe-area-context` `>=4 <6` | Optional peer | Insets for banners, flow cards, and Builder bars. | In development, `Userflow.init` warns once. Those surfaces draw under the status bar and home indicator. With the package installed but no `<SafeAreaProvider>`, cards use the device's initial insets and log a separate warning. |
38
+ | `@react-navigation/native` | Optional peer | Screen tracking and NAVIGATE routing (React Navigation). | Report screens yourself with `updateScreen` / `useUserflowScreen`. |
39
+ | `expo-router` | Optional peer | Screen tracking and NAVIGATE routing (Expo Router). | Report screens yourself with `updateScreen` / `useUserflowScreen`. |
40
+ | `@react-native-community/netinfo` `>=11 <13` | Optional peer | Instant offline→online reconnect. | Recovery still runs on the heartbeat and when the app returns to the foreground. If the JavaScript package is installed but `RNCNetInfo` is not in the binary, the SDK skips it instead of showing a red screen. |
41
+ | `expo-secure-store` | Optional peer | Persist the Builder pairing JWT in Keychain/Keystore. | The pairing token stays in memory and is lost when the process ends. The SDK loads it only when the `ExpoSecureStore` native module is registered. |
34
42
 
35
43
  Optional peers are declared `optional: true` in `peerDependenciesMeta`, so
36
- package managers will not warn if you skip them. Required peers (not marked
37
- optional) will warn — or fail the install — if they are missing.
44
+ package managers do not warn if you skip them.
45
+
46
+ A clean npm install prints no warning about optional peers, so install
47
+ `react-native-safe-area-context` and NetInfo yourself when you want the behavior
48
+ in the table. An app that already depends
49
+ on an older major (view-shot 4.0.3, NetInfo 9.x, safe-area 3.x, WebView 13.6.x
50
+ or 14.x) fails install with `ERESOLVE`.
38
51
 
39
52
  ### Expo Go is not supported
40
53
 
@@ -47,35 +60,8 @@ Use an **Expo development build** (or bare React Native). In development,
47
60
 
48
61
  ## Install
49
62
 
50
- ### Access
51
-
52
- Packages publish to the private `@userflow-tech` npm org. Userflow adds the
53
- client's npm user or organization to a **read-only team** on that org. The
54
- client then creates a **granular access token** on npm (Read-only, limited to
55
- `@userflow-tech`). A `404` from `npm install` or `npm view @userflow-tech/react-native`
56
- means the token is missing, or that user or org is not on the team. npm uses
57
- 404 for a private package the caller cannot see.
58
-
59
- ### npm, Yarn, and CI
60
-
61
- npm and Yarn 1 read `.npmrc`. Do not commit the token:
62
-
63
- ```ini
64
- @userflow-tech:registry=https://registry.npmjs.org/
65
- //registry.npmjs.org/:_authToken=${NPM_TOKEN}
66
- ```
67
-
68
- Yarn 2 and later ignore `.npmrc`. Add this to `.yarnrc.yml` instead:
69
-
70
- ```yaml
71
- npmScopes:
72
- userflow-tech:
73
- npmRegistryServer: 'https://registry.npmjs.org'
74
- npmAuthToken: '${NPM_TOKEN}'
75
- ```
76
-
77
- Set `NPM_TOKEN` in the environment that installs dependencies: a local shell,
78
- CI, and EAS (an EAS secret named `NPM_TOKEN`, available to the install step).
63
+ The package is public on npm. No token, `.npmrc`, or `.yarnrc.yml` setup is
64
+ needed with any package manager.
79
65
 
80
66
  ### Packages
81
67
 
@@ -88,11 +74,25 @@ npm install @userflow-tech/react-native @react-native-async-storage/async-storag
88
74
  cd ios && pod install
89
75
  ```
90
76
 
91
- `react-native-view-shot` is a **required peer** (`>=5.0.0`). It is a native
92
- module — rebuild after install (a Metro reload is not enough). On Expo SDK 54,
77
+ `react-native-webview` and `react-native-view-shot` (`^5.0.0`) are **required
78
+ peers**. npm 7+ and pnpm install them. Yarn 1 and
79
+ `npm install --legacy-peer-deps` do not, so the commands above install them in
80
+ the app. Both are native modules — rebuild after install (a Metro reload is not
81
+ enough). On Expo SDK 54,
93
82
  unpinned `npx expo install react-native-view-shot` resolves **4.0.3**, which
94
- does not satisfy the peer. Pin `^5.0.0` as in the command above. If `expo doctor`
95
- reports that pin, exclude it from Expo's version check:
83
+ does not satisfy the peer. Pin `^5.0.0` as in the command above. If the app
84
+ already depends on 4.0.3, that same command is what fixes it: installing the
85
+ SDK alone fails with
86
+
87
+ ```text
88
+ npm error Found: react-native-view-shot@4.0.3
89
+ npm error peer react-native-view-shot@"^5.0.0" from @userflow-tech/react-native
90
+ ```
91
+
92
+ Do not retry with `--force` or `--legacy-peer-deps`. That installs the SDK and
93
+ leaves 4.0.3 in place. If that already happened, `Userflow.init` warns in
94
+ development and names the installed version. If `expo doctor`
95
+ reports the `^5.0.0` pin, exclude it from Expo's version check:
96
96
 
97
97
  ```json
98
98
  {
@@ -114,10 +114,6 @@ npx expo install react-native-safe-area-context @react-native-community/netinfo
114
114
  # and either expo-router or @react-navigation/native, depending on your router
115
115
  ```
116
116
 
117
- For local tarball installs from this monorepo, pack with `pnpm pack:all` at the
118
- repo root and install the single `@userflow-tech/react-native` tarball. Steps:
119
- `docs/local-testing.md` in the monorepo.
120
-
121
117
  ### Install with an AI agent
122
118
 
123
119
  Cursor, Claude Code, and other agents that read Agent Skills can do this
@@ -143,7 +139,7 @@ Claude Code loads it from the plugin:
143
139
  import { Userflow, UserflowProvider } from '@userflow-tech/react-native';
144
140
 
145
141
  await Userflow.init({
146
- clientToken: 'ct_...', // from the Userflow dashboard → Environments. Never an esk_ secret key.
142
+ clientToken: 'ct_...', // from Settings → Environments in the Userflow dashboard. Never an esk_ secret key.
147
143
  // serverEndpoint: 'https://e.userflow.com', // default (US). EU: 'https://e.eu.userflow.com'
148
144
  // pairingEndpoint: 'https://e.userflow.com', // default; pairing stays on the US host
149
145
  });
@@ -162,6 +158,17 @@ NetInfo subscriptions). Call `Userflow.reset()` on logout to clear identity and
162
158
  disconnect. `reset()` does not apply a new `clientToken` or endpoint; restart
163
159
  the app to change those.
164
160
 
161
+ ### `init` options
162
+
163
+ | Option | Default | Description |
164
+ | ----------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------ |
165
+ | `clientToken` | — | **Required.** The `ct_…` client token from Settings → Environments. |
166
+ | `serverEndpoint` | `https://e.userflow.com` | Userflow host. EU environments use `https://e.eu.userflow.com`. |
167
+ | `pairingEndpoint` | `https://e.userflow.com` | Builder pairing host. Stays on the US host for EU environments too. |
168
+ | `onNavigate` | — | Handler for flow NAVIGATE actions. Wins over React Navigation, Expo Router, and Linking. See NAVIGATE below. |
169
+ | `appVersion` | Read from the native app | Override the host app version sent as `app_version`. |
170
+ | `appBuild` | Read from the native app | Build number sent as `app_build`, paired with `appVersion`. |
171
+
165
172
  ### Where to put `init`, `identify`, and `UserflowProvider`
166
173
 
167
174
  Call `Userflow.init` once, as early as you can, from the root component
@@ -231,14 +238,14 @@ to the device's initial insets:
231
238
 
232
239
  ### Call order
233
240
 
234
- | Call | Before `init()` resolves | Before `identify()` |
235
- | -------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
236
- | `identify` / `identifyAnonymous` | **Throws** `Call Userflow.init() before identify()` | — |
237
- | `group` | **Throws** `Call Userflow.init() before group()` | **Throws** `Call Userflow.identify() before group()` |
238
- | `updateUser` | **Throws** `Call Userflow.init() before updateUser()` | **Throws** `Call Userflow.identify() before updateUser()` |
239
- | `updateGroup` | **Throws** `Call Userflow.init() before updateGroup()` | **Throws** `Call Userflow.identify() before updateGroup()`; also requires a prior `group()` |
240
- | `track` | **Throws** `Call Userflow.init() before track()` | **Throws** `Call Userflow.identify() before track()`. Network, rate-limit, and unexpected SDK errors are isolated (the promise resolves; see [isolation.md](docs/isolation.md)) |
241
- | `updateScreen` | Silently buffers; included on the next channel join | Silently buffers |
241
+ | Call | Before `init()` resolves | Before `identify()` |
242
+ | -------------------------------- | ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
243
+ | `identify` / `identifyAnonymous` | **Throws** `Call Userflow.init() before identify()` | — |
244
+ | `group` | **Throws** `Call Userflow.init() before group()` | **Throws** `Call Userflow.identify() before group()` |
245
+ | `updateUser` | **Throws** `Call Userflow.init() before updateUser()` | **Throws** `Call Userflow.identify() before updateUser()` |
246
+ | `updateGroup` | **Throws** `Call Userflow.init() before updateGroup()` | **Throws** `Call Userflow.identify() before updateGroup()`; also requires a prior `group()` |
247
+ | `track` | **Throws** `Call Userflow.init() before track()` | **Throws** `Call Userflow.identify() before track()`. Network, rate-limit, and unexpected SDK errors are isolated (the promise resolves; see [isolation.md](https://unpkg.com/@userflow-tech/react-native/docs/isolation.md)) |
248
+ | `updateScreen` | Silently buffers; included on the next channel join | Silently buffers |
242
249
 
243
250
  Other rules:
244
251
 
@@ -255,7 +262,7 @@ Other rules:
255
262
  Same-id re-identify merges attributes.
256
263
  - Identity verification `signature` is optional in the type, but **required by
257
264
  the server** when the environment has identity verification enabled. See
258
- [docs/identity-verification.md](docs/identity-verification.md).
265
+ [docs/identity-verification.md](https://unpkg.com/@userflow-tech/react-native/docs/identity-verification.md).
259
266
 
260
267
  ## Localization
261
268
 
@@ -288,7 +295,7 @@ unmount or crash the host tree. `init` / `identify` still throw — a swallowed
288
295
  identify would look like success. Native crashes and errors in **your** children
289
296
  of `UserflowProvider` are out of scope.
290
297
 
291
- Details: [docs/isolation.md](docs/isolation.md).
298
+ Details: [docs/isolation.md](https://unpkg.com/@userflow-tech/react-native/docs/isolation.md).
292
299
 
293
300
  ## Screen tracking
294
301
 
@@ -381,13 +388,13 @@ function InviteScreen() {
381
388
 
382
389
  Targets inside a `ScrollView` / `FlatList` are scrolled into view automatically.
383
390
  `useUserflowScrollContainer` is only needed as an override for scroll libraries
384
- the fiber walk cannot drive — see [docs/anchoring.md](docs/anchoring.md).
391
+ the fiber walk cannot drive — see [docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md).
385
392
 
386
393
  In the Userflow builder, set the MANUAL step selector CSS to `#invite-teammates`
387
394
  (or `invite-teammates`). Use `useUserflowAnchor(id)` only when you need an
388
395
  explicit override (e.g. a third-party component that does not accept `testID`).
389
396
  Full contract, prop priority, missing-target behavior, and duplicate-id rules:
390
- [docs/anchoring.md](docs/anchoring.md).
397
+ [docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md).
391
398
 
392
399
  ## Builder preview & pairing
393
400
 
@@ -432,8 +439,8 @@ Deep-link / pairing helpers:
432
439
  | `PreviewModeBanner` | Mounted by `UserflowProvider` while paired. The top bar shows at once for a preview link, stays hidden for an element-selection link and after any pick until the next preview, and waits briefly on a link with no purpose. |
433
440
 
434
441
  Full scheme registration, Linking ownership, and verify commands:
435
- [docs/deep-linking.md](docs/deep-linking.md). Symptom table:
436
- [docs/troubleshooting.md](docs/troubleshooting.md#builder-preview--pairing).
442
+ [docs/deep-linking.md](https://unpkg.com/@userflow-tech/react-native/docs/deep-linking.md). Symptom table: the "Builder preview / pairing" section of
443
+ [docs/troubleshooting.md](https://unpkg.com/@userflow-tech/react-native/docs/troubleshooting.md).
437
444
 
438
445
  ## Connection resilience
439
446
 
@@ -464,33 +471,35 @@ Screen tracking ≠ action routing. React Navigation apps need
464
471
 
465
472
  ### `Userflow`
466
473
 
467
- | Member | Description |
468
- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
469
- | `init(config)` | Validate credentials, store config, start lifecycle + network controllers. |
470
- | `identify(userId, attributes?, options?)` | Identify a user; auto-sets `sdk` / `device_os` / `device_type` and `app_version` / `app_build` from init; `options.signature` for identity verification. |
471
- | `identifyAnonymous(attributes?, options?)` | Stable anonymous id via AsyncStorage; auto-sets `sdk` / `device_os` / `device_type` and `app_version` / `app_build`. |
472
- | `group(groupId, attributes?, options?)` | Associate the current user with a group. |
473
- | `updateUser(attributes?)` | Upsert attributes for the current user without re-identifying. |
474
- | `updateGroup(attributes?, options?)` | Upsert group / membership attributes without re-grouping. |
475
- | `track(name, attributes?, options?)` | Record a custom event (literal attribute values only); `userOnly` skips group. Misuse (`UserflowAuthError`) rejects; network / rate-limit / SDK bugs are isolated. |
476
- | `reset()` | Clear identity, client conditions, flow session; disconnect. |
477
- | `teardown()` | Stop AppState / NetInfo / screen-tracking subscriptions. |
478
- | `updateScreen(name)` | Report the active screen (prefer hooks when possible). |
479
- | `setOnNavigate(handler)` | Override or clear the host NAVIGATE handler. |
480
- | `reportActivity()` | Reset the idle timer; reconnect an idle-suspended socket. |
481
- | `didHandleURL(url)` | Handle a Userflow preview deep link (`…://?token=…`). |
482
- | `handlePairingUrl(url)` | Handle a Builder pairing deep link (`…://?pairing_token=…`). Works in every build; the signed JWT gates the session. |
483
- | `exitPreview()` | Leave Builder preview mode; disconnect the pairing websocket. Works in every build. |
484
- | `inspectPairingDeepLink(url)` | Classify a URL as pairing without consuming it; reports rejection reason. |
485
- | `isPairingAttempt(reason)` | True when a rejection was a recognisable pairing attempt worth reporting. |
486
- | `parsePairingDeepLink(url)` / `isUserflowPairingUrl(url)` | Parse / detect a pairing deep link. |
487
- | `getUserId()` | Current user id, or `null` before identify. |
488
- | `getAttributes()` | Copy of attributes for the current user. |
489
- | `getFlowSessionEngine()` | Mid-flow session engine (created lazily; attach runs after identify). |
490
- | `getHostAppVersion()` | Host app native version cached at init, or `null` before init / when unavailable. |
491
- | `getDeviceContext()` | Platform metadata cached during `init` (`sdk`, `deviceOs`, `deviceType`), or `null` before init. |
492
- | `logHostAppVersion()` | Log the host app's native version (not `SDK_VERSION`) and platform OS. Uses bundled device-info on bare RN. |
493
- | `logDeviceContext()` | Log cached user-attribute values (`sdk` / `device_os` / `device_type`). Requires `init`. |
474
+ | Member | Description |
475
+ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
476
+ | `init(config)` | Validate credentials, store config, start lifecycle + network controllers. |
477
+ | `identify(userId, attributes?, options?)` | Identify a user; auto-sets `sdk` / `device_os` / `device_type` and `app_version` / `app_build` from init; `options.signature` for identity verification. |
478
+ | `identifyAnonymous(attributes?, options?)` | Stable anonymous id via AsyncStorage; auto-sets `sdk` / `device_os` / `device_type` and `app_version` / `app_build`. |
479
+ | `group(groupId, attributes?, options?)` | Associate the current user with a group. |
480
+ | `updateUser(attributes?)` | Upsert attributes for the current user without re-identifying. |
481
+ | `updateGroup(attributes?, options?)` | Upsert group / membership attributes without re-grouping. |
482
+ | `track(name, attributes?, options?)` | Record a custom event (literal attribute values only); `userOnly` skips group. Misuse (`UserflowAuthError`) rejects; network / rate-limit / SDK bugs are isolated. |
483
+ | `reset()` | Clear identity, the stored anonymous id, client conditions, and flow and banner sessions; disconnect. |
484
+ | `teardown()` | Stop AppState / NetInfo / screen-tracking subscriptions. |
485
+ | `updateScreen(name)` | Report the active screen (prefer hooks when possible). |
486
+ | `setOnNavigate(handler)` | Override or clear the host NAVIGATE handler. |
487
+ | `reportActivity()` | Reset the idle timer; reconnect an idle-suspended socket. |
488
+ | `didHandleURL(url)` | Handle a Userflow preview deep link (`…://?token=…`). |
489
+ | `handlePairingUrl(url)` | Handle a Builder pairing deep link (`…://?pairing_token=…`). Works in every build; the signed JWT gates the session. |
490
+ | `exitPreview()` | Leave Builder preview mode; disconnect the pairing websocket. Works in every build. |
491
+ | `setScrollPadding(padding)` | Sticky header/footer insets used when scrolling tooltip targets into view. |
492
+ | `setCustomScrollIntoView(fn)` | Replace scroll-into-view (e.g. unsupported scroll libraries). Pass `null` to restore the default. |
493
+ | `setSurfaceInteractionBlocking(enabled)` | `true` (default) blocks taps on the app under tooltip backdrops. `false` lets them through. Modals always block. |
494
+ | `setEvalJsContext(values)` | Values exposed to Evaluate JavaScript as `window.userflowContext.host`. Replaces the previous values. |
495
+ | `getUserId()` | Current user id, or `null` before identify. |
496
+ | `getAttributes()` | Copy of attributes for the current user. |
497
+ | `getFlowSessionEngine()` | Mid-flow session engine (created lazily; attach runs after identify). |
498
+ | `getBannerSessionEngine()` | Banner session engine (created lazily; attach runs after identify). |
499
+ | `getHostAppVersion()` | Host app native version cached at init, or `null` before init / when unavailable. |
500
+ | `getDeviceContext()` | Platform metadata cached during `init` (`sdk`, `deviceOs`, `deviceType`), or `null` before init. |
501
+ | `logHostAppVersion()` | Log the host app's native version (not `SDK_VERSION`) and platform OS. Uses bundled device-info on bare RN. |
502
+ | `logDeviceContext()` | Log cached user-attribute values (`sdk` / `device_os` / `device_type`). Requires `init`. |
494
503
 
495
504
  ### Hooks and components
496
505
 
@@ -503,8 +512,19 @@ Screen tracking ≠ action routing. React Navigation apps need
503
512
  | `useUserflowScreen(name)` | Report the active screen from a React tree. |
504
513
  | `useUserflowAnchor(id)` | Optional override: register a view as a MANUAL target (beats `userflowId` / `testID`). |
505
514
  | `useUserflowScrollContainer(onScroll?)` | Optional override: register a `ScrollView` for tooltip scroll-into-view. |
506
- | `Userflow.setScrollPadding(padding)` | Sticky chrome insets when scrolling targets into view. |
507
- | `Userflow.setCustomScrollIntoView(fn)` | Replace scroll-into-view (e.g. unsupported scroll libraries). |
515
+ | `EmbedBannerPlaceholder` | Slot for a banner placed on an element (`<EmbedBannerPlaceholder id="welcome-banner" />` matches the selector `#welcome-banner`). Renders nothing until a matching banner is active. Top and bottom banners ignore it. |
516
+
517
+ ### Pairing helpers
518
+
519
+ Named exports, not members of `Userflow`. Use them when your app owns `Linking`
520
+ and forwards URLs itself.
521
+
522
+ | Export | Description |
523
+ | ----------------------------- | ------------------------------------------------------------------------- |
524
+ | `inspectPairingDeepLink(url)` | Classify a URL as pairing without consuming it; reports rejection reason. |
525
+ | `isPairingAttempt(reason)` | True when a rejection was a recognisable pairing attempt worth reporting. |
526
+ | `parsePairingDeepLink(url)` | Parse a pairing deep link. |
527
+ | `isUserflowPairingUrl(url)` | Detect a pairing deep link. |
508
528
 
509
529
  ### Subpath exports
510
530
 
@@ -524,20 +544,22 @@ Screen tracking ≠ action routing. React Navigation apps need
524
544
 
525
545
  ## Guides in this package
526
546
 
527
- | Doc | Topic |
528
- | -------------------------------------------------------------- | ----------------------------------------------------------- |
529
- | [docs/anchoring.md](docs/anchoring.md) | Manual targets, scroll containers, missing-target behavior |
530
- | [docs/isolation.md](docs/isolation.md) | What “isolated as possible” means; overlay + `track` guards |
531
- | [docs/deep-linking.md](docs/deep-linking.md) | Builder preview URLs and pairing sessions |
532
- | [docs/identity-verification.md](docs/identity-verification.md) | HMAC signature on your backend |
533
- | [docs/troubleshooting.md](docs/troubleshooting.md) | Symptom → cause for common integration failures |
534
- | [docs/theme-mapping.md](docs/theme-mapping.md) | Web → RN theme token mapping (contributor-facing) |
547
+ These guides also ship in the package, under
548
+ `node_modules/@userflow-tech/react-native/docs/`.
549
+
550
+ | Doc | Topic |
551
+ | ------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- |
552
+ | [docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md) | Manual targets, scroll containers, missing-target behavior |
553
+ | [docs/isolation.md](https://unpkg.com/@userflow-tech/react-native/docs/isolation.md) | What “isolated as possible” means; overlay + `track` guards |
554
+ | [docs/deep-linking.md](https://unpkg.com/@userflow-tech/react-native/docs/deep-linking.md) | Builder preview URLs and pairing sessions |
555
+ | [docs/identity-verification.md](https://unpkg.com/@userflow-tech/react-native/docs/identity-verification.md) | HMAC signature on your backend |
556
+ | [docs/troubleshooting.md](https://unpkg.com/@userflow-tech/react-native/docs/troubleshooting.md) | Symptom → cause for common integration failures |
535
557
 
536
558
  ## Experimental
537
559
 
538
560
  - AUTO / codeless element targeting (exact-id fast path + scored matcher;
539
561
  scored path needs Babel full mode to populate the auto-anchor registry).
540
- See [docs/anchoring.md](docs/anchoring.md).
562
+ See [docs/anchoring.md](https://unpkg.com/@userflow-tech/react-native/docs/anchoring.md).
541
563
  - Build-time stamping with `@userflow-tech/react-native/babel-plugin`. Jest
542
564
  runs the plugin too, which changes snapshots. Read
543
565
  [Babel plugin and snapshot tests](#babel-plugin-and-snapshot-tests) before
@@ -546,10 +568,14 @@ Screen tracking ≠ action routing. React Navigation apps need
546
568
  ## Install diagnostics
547
569
 
548
570
  In development, the first `Userflow.init` checks native module registration and
549
- logs one warning when something the SDK needs is not linked. It does not
550
- `require` an unlinked package. Rebuild the native app after fixing the list; a
551
- Metro reload is not enough. The warning looks like this (only the missing lines
552
- are included):
571
+ whether `react-native-safe-area-context` is installed. Missing native modules
572
+ share one warning. A missing safe-area package gets its own warning, because
573
+ flows still run and draw under the status bar. It does not `require` an
574
+ unlinked package (including NetInfo, whose import throws when `RNCNetInfo` is
575
+ absent). Rebuild the native app after fixing a native-module line; a Metro
576
+ reload is not enough. Installing safe-area-context also needs a rebuild, then a
577
+ `<SafeAreaProvider>` around the app. The warnings look like this (only the
578
+ missing lines are included):
553
579
 
554
580
  ```text
555
581
  [Userflow] Native setup is incomplete. Flows may not run until you fix the following, then rebuild the app (a Metro reload is not enough):
@@ -557,7 +583,12 @@ are included):
557
583
  - react-native-view-shot is not linked, so Builder element selection gets no screenshot. Install react-native-view-shot@^5.0.0 and rebuild. On Expo SDK 54, pin ^5.0.0; an unpinned npx expo install resolves 4.0.3.
558
584
  - react-native-webview is not linked. Evaluate JavaScript and WebView banners will not run. Install it (npx expo install react-native-webview) and rebuild.
559
585
  - react-native-device-info is not linked, so app_version and app_build are not sent. npm install react-native-device-info, then rebuild.
560
- See docs/troubleshooting.md.
586
+ See node_modules/@userflow-tech/react-native/docs/troubleshooting.md.
587
+ ```
588
+
589
+ ```text
590
+ [Userflow] react-native-safe-area-context is not installed, so banners, flow cards, and Builder bars draw under the status bar and home indicator. Install it (npx expo install react-native-safe-area-context) and wrap the app in <SafeAreaProvider>.
591
+ See node_modules/@userflow-tech/react-native/docs/troubleshooting.md.
561
592
  ```
562
593
 
563
594
  Passing an environment secret key as `clientToken` throws:
@@ -572,8 +603,11 @@ Pairing stays on `https://e.userflow.com`.
572
603
 
573
604
  `expo prebuild` warns when the config plugin has no `schemeId`, when
574
605
  `react-native-view-shot` is missing or older than 5, and (on Expo SDK 53 and
575
- older) when AsyncStorage or WebView is not a direct dependency. Passing an
576
- environment UUID as `schemeId` fails the prebuild:
606
+ older) when AsyncStorage, WebView, or safe-area-context is not a direct
607
+ dependency. Expo SDK 54 and later autolink safe-area-context when another
608
+ package such as Expo Router brought it in, so prebuild stays quiet in that
609
+ case. `Userflow.init` still warns when the package itself cannot be loaded.
610
+ Passing an environment UUID as `schemeId` fails the prebuild:
577
611
 
578
612
  ```text
579
613
  [@userflow-tech/react-native] schemeId "550e8400-e29b-41d4-a716-446655440000" looks like an environment id. Pass the scheme_id value that starts with userflow- (an opaque id from the dashboard), not the environment UUID.
@@ -681,4 +715,4 @@ the files it lists, not the components a test renders.
681
715
  - `INPUT_VALUE` / `FILLED_IN_INPUT` client conditions
682
716
 
683
717
  Flows gated on unsupported client conditions stay unknown and **never start**.
684
- See [docs/troubleshooting.md](docs/troubleshooting.md).
718
+ See [docs/troubleshooting.md](https://unpkg.com/@userflow-tech/react-native/docs/troubleshooting.md).
package/docs/anchoring.md CHANGED
@@ -16,7 +16,7 @@ Dashboard MANUAL selectors store a CSS-like string on `step.selector.css` (or
16
16
  `condition.selector.css`). The SDK resolves it with:
17
17
 
18
18
  ```typescript
19
- // packages/react-native/src/anchors/resolveManualAnchorId.ts
19
+ // src/anchors/resolveManualAnchorId.ts
20
20
  return trimmed.startsWith('#') ? trimmed.slice(1) : trimmed;
21
21
  ```
22
22
 
@@ -318,7 +318,7 @@ Modals always block — the full-screen backdrop is a `Pressable` hit target.
318
318
  Pass `Userflow.setSurfaceInteractionBlocking(false)` to restore the legacy
319
319
  pass-through tooltip backdrop (`pointerEvents="none"` on dim bands) so the
320
320
  host screen stays scrollable and tappable underneath. The `tooltipTargetBlocked`
321
- hole seal is unchanged — it was independent of band pass-through before UMD-74.
321
+ hole seal is unchanged.
322
322
 
323
323
  When a tooltip step has `backdrop: false`, the cutout still mounts with dim
324
324
  opacity `0` so interaction blocking applies without a visible dim.
@@ -341,7 +341,7 @@ rect** via a pass-through bridge around app content (`TargetPressBridge` in
341
341
  ELEMENT `CLICK` / `NOT_CLICK` auto-start conditions use the same bridge
342
342
  (`elementTapMonitor`) and do not steal the press either. `MOUSEDOWN` and
343
343
  `DISABLED` conditions are still unsupported — see
344
- [client-conditions.md](client-conditions.md) / [troubleshooting.md](troubleshooting.md).
344
+ [troubleshooting.md](troubleshooting.md).
345
345
 
346
346
  ## Missing-target behavior
347
347
 
@@ -405,4 +405,3 @@ The Userflow overlay is a _sibling_ of the scan root, so SDK-owned `testID`s
405
405
 
406
406
  - [troubleshooting.md](troubleshooting.md) — “flow never appeared” checklist
407
407
  - [theme-mapping.md](theme-mapping.md) — chrome tokens that affect tooltip UI
408
- - Repo `docs/client-conditions.md` (monorepo only) — track / toggle protocol internals