> ## Documentation Index
> Fetch the complete documentation index at: https://docs.userpilot.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Auto Capture

> Configure auto capture for the Userpilot React Native SDK

Configure automatic screen and interaction capture for React Native applications using the Userpilot React Native SDK.

The Userpilot React Native SDK can automatically capture screen views and user interactions, enabling analytics and engagement without manual screen and event tracking. Screen capture runs through a navigation adapter; interaction capture requires the Userpilot Babel plugin for build-time instrumentation.

***

## SDK Configuration

Auto capture is **off by default**. Auto capture behavior is configured through options passed to `Userpilot.setup` during SDK initialization.

```ts theme={null}
import * as Userpilot from '@userpilot/react-native';

await Userpilot.setup('<APP_TOKEN>', {
  logging: true,
  enableScreenAutoCapture: true,
  enableInteractionAutoCapture: true,
  enableInteractionAccessibilityLabelCapture: true,
  enableInteractionTextCapture: true,
  enableInteractionValueCapture: false,
  enableInteractionCustomPropsCapture: true,
  navigation: {
    library: Userpilot.NavigationLibraryType.ReactNavigation,
    navigation: navigationRef,
  },
});
```

| **Option**                                   | **Type**           | **Default** | **Description**                                                                                                                                                                                                                                                                                                 |
| -------------------------------------------- | ------------------ | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enableScreenAutoCapture`                    | `boolean`          | `false`     | Enables automatic screen tracking from the configured navigation adapter.                                                                                                                                                                                                                                       |
| `enableInteractionAutoCapture`               | `boolean`          | `false`     | Enables automatic interaction tracking: tap, value-change, selection-change, text-change, and `view_presented`. Requires the Babel plugin.                                                                                                                                                                      |
| `enableInteractionAccessibilityLabelCapture` | `boolean`          | `true`      | Captures accessibility metadata (`accessibilityLabel` / `testID` as `accessibility_label` / `accessibility_identifier`).                                                                                                                                                                                        |
| `enableInteractionTextCapture`               | `boolean`          | `true`      | Captures visible text labels (button titles, modal messages, etc.). Set `false` to strip those text fields from interaction payloads.                                                                                                                                                                           |
| `enableInteractionValueCapture`              | `boolean`          | `false`     | Captures user-input values (toggle state, selected values, text length metadata). Off by default for privacy. Position fields such as `selected_index` are still captured when available.                                                                                                                       |
| `enableInteractionCustomPropsCapture`        | `boolean`          | `false`     | Includes up to five primitive custom React props as flat metadata. Names are normalized, sorted alphabetically, and the first five are selected. Only strings, finite numbers, booleans, and `null` are captured; objects, arrays, functions, symbols, bigints, `undefined`, `NaN`, and infinities are dropped. |
| `navigation`                                 | `NavigationOption` | *undefined* | Screen tracking adapter (see [Screen Capture](#screen-capture)). Required when `enableScreenAutoCapture` is `true`.                                                                                                                                                                                             |

Example: disable all text and accessibility label capture for maximum privacy:

```ts theme={null}
await Userpilot.setup('<APP_TOKEN>', {
  enableScreenAutoCapture: true,
  enableInteractionAutoCapture: true,
  enableInteractionTextCapture: false,
  enableInteractionAccessibilityLabelCapture: false,
  navigation: {
    library: Userpilot.NavigationLibraryType.ReactNavigation,
    navigation: navigationRef,
  },
});
```

Because accessibility labels can mirror on-screen text, set `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.

<Tip>
  **Note**

  Auto Capture automatically tracks screen views and ignores manually sent `screen` events while enabled.

  If you are migrating from manual screen tracking, please review the migration guide [here](https://docs.userpilot.com/data-events/mobile-screen-tracking/mobile-screen-auto-capture#important).
</Tip>

***

## 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:

```sh theme={null}
npm install --save-dev @userpilot/babel-plugin-react-native-autocapture
```

Add it to your app's `babel.config.js`:

```js theme={null}
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: ['@userpilot/babel-plugin-react-native-autocapture'],
};
```

Then reset the Metro cache once:

```sh theme={null}
npx react-native start --reset-cache
```

The plugin wraps interactive React Native JSX (`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`:

```js theme={null}
module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    [
      '@userpilot/babel-plugin-react-native-autocapture',
      {
        targets: [
          {
            source: 'react-native-paper',
            components: [
              { name: 'Button', interactionType: 'tap' },
              { name: 'IconButton', interactionType: 'tap' },
            ],
          },
        ],
      },
    ],
  ],
};
```

***

## Screen Capture

When `enableScreenAutoCapture` 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`:

```ts theme={null}
export const NavigationLibraryType = {
  ReactNavigation: 'react-navigation',
  ReactNativeNavigation: 'react-native-navigation',
  ExpoRouter: 'expo-router',
} as const;
```

<Tip>
  **Manual screens**

  `Userpilot.screen('<title>')` is ignored whenever `enableScreenAutoCapture` is `true`, including when `navigation` is missing. To send screens manually, leave `enableScreenAutoCapture: false` and call `Userpilot.screen(...)` yourself.
</Tip>

### React Navigation

Pass the `NavigationContainer` ref as `navigation`. The SDK listens to route state changes and uses the current route name as the screen name.

```tsx theme={null}
import { useEffect } from 'react';
import { useNavigationContainerRef } from '@react-navigation/native';
import * as Userpilot from '@userpilot/react-native';

export function App() {
  const navRef = useNavigationContainerRef();

  useEffect(() => {
    Userpilot.setup('<APP_TOKEN>', {
      logging: true,
      enableScreenAutoCapture: true,
      enableInteractionAutoCapture: true,
      enableInteractionCustomPropsCapture: true,
      navigation: {
        library: Userpilot.NavigationLibraryType.ReactNavigation,
        navigation: navRef,
      },
    });

    return () => Userpilot.stopAutocapture();
  }, [navRef]);
}
```

### React Native Navigation (Wix)

For `react-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.

```tsx theme={null}
import { Navigation } from 'react-native-navigation';
import * as Userpilot from '@userpilot/react-native';

Navigation.registerComponent('MainScreen', () => MainScreen);

Userpilot.setup('<APP_TOKEN>', {
  enableScreenAutoCapture: true,
  enableInteractionAutoCapture: true,
  navigation: {
    library: Userpilot.NavigationLibraryType.ReactNativeNavigation,
  },
});
```

### Expo Router

Expo Router does not expose a `NavigationContainer` 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.

```tsx theme={null}
// app/_layout.tsx
import { Slot } from 'expo-router';
import * as Userpilot from '@userpilot/react-native';

export default function RootLayout() {
  return (
    <>
      <Userpilot.UserpilotExpoRouterTracker />
      <Slot />
    </>
  );
}
```

```ts theme={null}
Userpilot.setup('<APP_TOKEN>', {
  enableScreenAutoCapture: true,
  enableInteractionAutoCapture: true,
  navigation: { library: Userpilot.NavigationLibraryType.ExpoRouter },
});
```

### Missing navigation configuration

Screen auto capture never falls back to manual screen names. If `enableScreenAutoCapture` is `true` but `navigation` is omitted, the SDK logs this warning and does not capture screen events:

```text theme={null}
[Userpilot:autocapture] screen autocapture is enabled, but `navigation` is not configured. Set the `navigation` option in Userpilot.setup() to your navigation library.
```

Calls to `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:

```ts theme={null}
await Userpilot.setup('<APP_TOKEN>', {
  enableScreenAutoCapture: false,
  enableInteractionAutoCapture: true,
});

Userpilot.screen('Profile');
```

When interaction auto capture is enabled, these manual calls also keep the current-screen context for later interaction events.

***

## Interaction Capture

When `enableInteractionAutoCapture: true`, the SDK emits an event for the following user interactions (requires the Babel plugin):

| **Interaction type** | **Emitted for**                                                                                                                                                                                                 |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tap`                | `Pressable`, `TouchableOpacity`, `TouchableHighlight`, `TouchableWithoutFeedback`, `TouchableNativeFeedback`, and `Button`. Includes button copy and the original `onPress` callback identifier when available. |
| `text_field_changed` | `TextInput` (cached per field; one event per field when leaving the screen so you don't get a flood of keystroke events). Typed text is not captured.                                                           |
| `switch_changed`     | `Switch` (captures the new boolean value as `is_checked` when value capture is enabled).                                                                                                                        |
| `view_presented`     | `Modal` presentation (payload includes `dialog_title` / `dialog_message` when discoverable).                                                                                                                    |
| Selection / value    | `CheckBox`, `RadioButton`, list items, `Tab`, and `MenuItem` — available through third-party UI libraries via the plugin's `targets` option (see [Add third-party libraries](#add-third-party-libraries)).      |

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, pass `enableInteractionValueCapture: 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:

| **Key**                                           | **Meaning**                                                           |
| ------------------------------------------------- | --------------------------------------------------------------------- |
| `target_class`                                    | Classified component type (e.g. `Pressable`, `Modal`)                 |
| `target_text`                                     | Primary visible label / title for the control                         |
| `hierarchy`                                       | Semicolon-separated leaf-to-root path (see below)                     |
| `accessibility_label`, `accessibility_identifier` | Flattened accessibility metadata from `accessibilityLabel` / `testID` |
| `selected_index`                                  | List/tab/selection index                                              |
| `is_checked`, `text_length`                       | Captured values when value capture is enabled                         |
| `dialog_title`, `dialog_message`                  | Presented modal title and message when discoverable                   |

### `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's `accessibility_label`. Omitted when `enableInteractionAccessibilityLabelCapture: false`, under `<UserpilotIgnoreInteractions>`, or when no label exists. Ancestor segments deliberately omit it.
* `attr__id` — leaf: the `accessibility_identifier` (`testID`), falling back to `accessibility_label`. Ancestors: any stamped accessibility id from JSX ancestry.
* `attr__index` — zero-based sibling index under the same parent.

Segments are joined with `;`. 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.

```tsx theme={null}
import * as Userpilot from '@userpilot/react-native';

<Userpilot.UserpilotRedactText>
  <Button title="Account 4242 4242 4242 4242" onPress={confirm} />
</Userpilot.UserpilotRedactText>
```

`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.

```tsx theme={null}
import * as Userpilot from '@userpilot/react-native';

<Userpilot.UserpilotIgnoreInteractions>
  <Button title="Internal-only action" onPress={internalAction} />
</Userpilot.UserpilotIgnoreInteractions>
```

Use `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:

```tsx theme={null}
import * as Userpilot from '@userpilot/react-native';

const IgnoredButton = Userpilot.withUserpilotIgnoreInteractions(Button);

// Drop in <IgnoredButton /> wherever you need the ignored variant.
```

***

## Runtime Controls

To stop or resume collection without changing options — for example a "private mode" — use:

```ts theme={null}
Userpilot.stopAutocapture();   // stops capture; preserves options
Userpilot.resumeAutocapture(); // resumes capture
```

Both calls are idempotent. They preserve the options supplied to `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.
* **`navigation` required for screen auto capture** — if `enableScreenAutoCapture` is `true` but `navigation` is omitted, no screen events are captured. Manual `Userpilot.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 `targets` option.
* **Expo Router tracker** — `<UserpilotExpoRouterTracker />` must be mounted once in the root layout; omitting it means no automatic screen events for Expo Router.

<Frame>
  [**For any questions or concerns please reach out to support@userpilot.com**](mailto:support@userpilot.com)
</Frame>
