SDK Configuration
Auto capture is off by default. Auto capture behavior is configured through options passed toUserpilot.setup during SDK initialization.
Example: disable all text and accessibility label capture for maximum privacy:
enableInteractionAccessibilityLabelCapture: false whenever you set enableInteractionTextCapture: false.
enableInteractionCustomPropsCapture applies only to extra React props copied into the event payload. First-class auto capture fields such as target_class, target_view_name, target_text, target_action, hierarchy, accessibility_label, and accessibility_identifier are controlled by their dedicated options above and are not considered custom props. If a custom prop serializes to a reserved payload key, for example target_action or targetAction, the custom prop is dropped and the SDK-owned value is kept. Eligible names are normalized to their payload form, sorted with a locale-independent alphabetical comparison, and limited to the first five distinct names. The five names reserve their positions before values are validated, so a temporarily invalid or undefined value does not allow a sixth name to replace it.
Build-Time Instrumentation
Interaction auto capture also needs build-time instrumentation in the app. Without the Babel plugin, interaction events are not emitted — the SDK does not monkey-patch React Native at runtime. Install the plugin:babel.config.js:
Pressable, Touchable*, Button, TextInput, Switch, Modal) at compile time. The original component is rendered untouched, and refs and props pass through unchanged.
Screen capture works without the Babel plugin. This step is required only for interaction capture (enableInteractionAutoCapture: true).
Add third-party libraries
To instrument components from third-party UI libraries (e.g.react-native-paper), pass them as targets in babel.config.js:
Screen Capture
WhenenableScreenAutoCapture is true and a navigation adapter is configured, the SDK emits a screen event whenever the active route changes. The event sent to the native bridge contains the screen name (the current route name from your navigation library).
Deduplication is keyed on the route key (not the screen name), so navigating from /profile/1 to /profile/10 correctly produces two screen events.
Pick the adapter matching your navigation library. The SDK uses lazy require(), so customers who haven’t installed a given navigation library never pull it into their bundle. The adapter is selected via NavigationLibraryType:
React Navigation
Pass theNavigationContainer ref as navigation. The SDK listens to route state changes and uses the current route name as the screen name.
React Native Navigation (Wix)
Forreact-native-navigation, set the library and register your RNN screens as usual. No navigation handle is needed because the SDK listens to Navigation.events().registerComponentDidAppearListener(...) globally.
Expo Router
Expo Router does not expose aNavigationContainer ref. Set the library and mount <UserpilotExpoRouterTracker /> once in your root app/_layout.tsx. The SDK uses the exact usePathname() value as the screen name, so paths such as /profile/1 and /profile/10 are captured as different screen events.
Missing navigation configuration
Screen auto capture never falls back to manual screen names. IfenableScreenAutoCapture is true but navigation is omitted, the SDK logs this warning and does not capture screen events:
Userpilot.screen('Profile') are ignored whenever screen auto capture is enabled, including when the navigation configuration is missing. This prevents manual names from silently becoming the source for an automatic screen stream.
If you want to track screens manually, leave screen auto capture disabled and call Userpilot.screen(...) as usual:
Interaction Capture
WhenenableInteractionAutoCapture: true, the SDK emits an event for the following user interactions (requires the Babel plugin):
Each interaction maps to a high-level analytics category (
tap, text_change, selection_change, value_change, view_presented) — that is the event name that reaches your analytics backend.
Component Value Gating
By default, user-input values are stripped from event payloads — toggle states, selected values, and text length metadata do not leave the device. To include them, passenableInteractionValueCapture: true in Userpilot.setup. Position and configuration fields (selected_index) are always captured because they don’t reveal user input.
Privacy Controls
Event Metadata Keys
Common fields on interaction events:hierarchy Format
hierarchy is a denormalized leaf-to-root string. Each semicolon-delimited segment looks like:
ComponentName:attr__id="…",attr__index="N",attr__desc="…"
attr__desc— only the leaf segment’saccessibility_label. Omitted whenenableInteractionAccessibilityLabelCapture: false, under<UserpilotIgnoreInteractions>, or when no label exists. Ancestor segments deliberately omit it.attr__id— leaf: theaccessibility_identifier(testID), falling back toaccessibility_label. Ancestors: any stamped accessibility id from JSX ancestry.attr__index— zero-based sibling index under the same parent.
;. Under <UserpilotRedactText>, hierarchy is preserved; visible text fields are replaced with **** rather than dropped from the path attributes controlled by accessibility capture.
UserpilotRedactText
Wraps a subtree so auto capture replaces every text label it would otherwise capture (target_text, dialog_title, dialog_message, etc.) with ****. The interaction event still fires — the component is still identified by type, position, and structure — only the human-readable text is masked.
Capture configuration is applied before redacted values leave the SDK. If enableInteractionTextCapture: false, text fields such as target_text are removed from the payload instead of being sent as ****. If enableInteractionAccessibilityLabelCapture: false, accessibility fields are removed from the payload and from hierarchy attributes.
hierarchy remains on redacted events. Remove the wrapper to disable masking for that subtree.
UserpilotIgnoreInteractions
Wraps a subtree so auto capture skips interaction events whose hit-tested component lies inside it (taps, value changes, text-field updates tied to that field, etc.). Route-driven screen events are not suppressed by UserpilotIgnoreInteractions — they come from the navigation adapter, not from component hit testing.
The host app’s own gesture handlers (onPress, …) still run — only auto capture ignores those interactions.
UserpilotIgnoreInteractions for interactions whose mere occurrence would leak intent (e.g. tapping a row in a contacts list, where row position correlates 1:1 with contact identity).
Both wrappers are passive markers — they render children unchanged. Wrap the outermost component you want affected; descendants of the wrapper are covered, ancestors are not.
withUserpilotIgnoreInteractions(Component) returns a wrapped component that is always ignored. Use it when you want a single, named, ignored version of a component:
Runtime Controls
To stop or resume collection without changing options — for example a “private mode” — use:Userpilot.setup(...).
While stopped, no screen or interaction events are recorded regardless of other configuration. This is a global toggle that overrides per-subtree settings.
Limitations
- Babel plugin required for interactions — without
@userpilot/babel-plugin-react-native-autocapture, interaction events are not emitted. Screen capture does not require the plugin. navigationrequired for screen auto capture — ifenableScreenAutoCaptureistruebutnavigationis omitted, no screen events are captured. ManualUserpilot.screen(...)calls are also ignored while screen auto capture is enabled.- Third-party UI libraries — components outside the built-in React Native set are not instrumented unless listed in the Babel plugin’s
targetsoption. - Expo Router tracker —
<UserpilotExpoRouterTracker />must be mounted once in the root layout; omitting it means no automatic screen events for Expo Router.