npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@userpilot/babel-plugin-react-native-autocapture

v1.0.0

Published

Babel plugin that instruments React Native JSX for Userpilot autocapture. Wraps Pressable, Touchable*, TextInput, Switch, Modal at build time so autocapture works without runtime monkey-patching.

Readme

Userpilot React Native Autocapture Babel Plugin

npm License: MIT

Part of the Userpilot React Native monorepo. The runtime SDK that consumes the events emitted by this plugin lives at @userpilot/react-native. For the cross-cutting autocapture guide (screens, interactions, privacy, hierarchy format), see docs/Autocapture.md.

The Userpilot React Native Autocapture Babel Plugin is the build-time companion to @userpilot/react-native. It instruments your app's JSX at compile time so the SDK can emit unified InteractionEvent payloads — bit-for-bit identical to the iOS and Android Userpilot SDKs — without any runtime monkey-patching of React Native components.

This document provides a step-by-step walkthrough of the installation and configuration process, plus reference material for the plugin's options, supported targets, and emitted payloads.

🚀 Getting Started

Prerequisites

React Native — your application should use React Native version 0.73 or above. Applications using Expo should use version 50 or above. The New Architecture is fully supported.

Userpilot React Native SDK — this plugin emits JSX that imports a runtime helper from @userpilot/react-native. The runtime SDK must be installed and initialized via Userpilot.setup(token, { enableInteractionAutoCapture: true }) for events to be dispatched.

npm install @userpilot/react-native

Node.js — the plugin runs in your build pipeline (Babel/Metro). Node 18+ is required.

Installation

Add the plugin to your application's dev dependencies.

  1. In your app's root directory, install the plugin:
    npm install --save-dev @userpilot/babel-plugin-react-native-autocapture
  2. Reset the Metro cache once after the first install so the new transform applies to every source file:
    npx react-native start --reset-cache

Configuring babel.config.js

Add the plugin to your application's babel.config.js. The plugin must run inside React Native's Babel pipeline; it does not apply to files that bypass Babel (e.g. precompiled native libraries).

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

That's it. As long as your app calls Userpilot.setup(token, { enableInteractionAutoCapture: true }) at startup, every supported JSX tag in your app emits autocapture events on each interaction.

What the Plugin Does

For each matching JSX node, the plugin rewrites the call site to render through a tiny React.forwardRef runtime helper exported from @userpilot/react-native:

// before
<Pressable onPress={handlePress}>Login</Pressable>

// after (conceptually — actual binding is uniqued per file)
<__userpilotWrap
  __upTag={Pressable}
  __upInteractionType="tap"
  __upElementType="Pressable"
  onPress={handlePress}>
  Login
</__userpilotWrap>

The helper dispatches the unified InteractionEvent and renders the original component with all original props and refs intact. The transform is idempotent — re-running Babel on already-instrumented JSX is a no-op.

Plugin Options

Pass options to the plugin via the babel.config.js array form:

module.exports = {
  presets: ['module:@react-native/babel-preset'],
  plugins: [
    [
      '@userpilot/babel-plugin-react-native-autocapture',
      {
        enabled: true,
        presets: ['react-native'],
        targets: [],
        exclude: [],
        logging: false,
      },
    ],
  ],
};
    Options:

| Option | Type | Description | Default | | ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | | enabled | boolean | Master switch. Set to false to disable instrumentation entirely (e.g. in a build profile that ships without analytics). | true | | presets | ('react-native')[] | Built-in component groups to wrap. The react-native preset covers Pressable, Touchable*, Button, TextInput, Switch, and Modal. | ['react-native'] | | targets | TargetGroup[] | Additional component groups to wrap. Layered on top of presets — useful for opting third-party UI libraries in. See Adding Third-Party Targets. | [] | | exclude | string[] | Literal path substrings to skip. Glob and regex syntax is not supported; node_modules is always implicitly excluded. | [] | | logging | boolean | Log per-file/per-match diagnostics to stderr. Use only when investigating why a node was not wrapped. | false |

Built-in Presets

The react-native preset covers the standard interactive primitives shipped with React Native:

| Preset name | Components covered | Default interactionType | | -------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------- | | react-native | Pressable, TouchableOpacity, TouchableHighlight, TouchableWithoutFeedback, TouchableNativeFeedback, Button | tap | | react-native | TextInput | text_field_changed | | react-native | Switch | switch_changed | | react-native | Modal | view_presented |

These mirror the Android InteractionType enum exactly so the wire payload stays consistent across iOS, Android, and React Native.

Adding Third-Party Targets

To instrument components from third-party UI libraries (e.g. react-native-paper), declare them in targets. The source field matches the import source string — both literal strings and RegExp are supported. The components array names the imports to wrap and the interactionType they emit.

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' },
              { name: 'TextInput', interactionType: 'text_field_changed' },
            ],
          },
          {
            source: '@react-native-community/checkbox',
            components: [
              { name: 'CheckBox', interactionType: 'checkbox_selected' },
            ],
          },
        ],
      },
    ],
  ],
};

Each TargetComponent accepts:

| Field | Type | Description | | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------- | | name | string | The local binding used in JSX (e.g. "Button"). | | interactionType | string | The Userpilot InteractionType string emitted for this component. Must match a value the runtime understands. | | elementType | string | Optional. Override the elementType recorded on the payload (defaults to name). |

Excluding Files

To skip specific paths from instrumentation, pass literal path substrings via exclude. To target one file, use its full project-relative path. Each entry is matched literally against the filename Babel provides, so glob and regular-expression syntax is not supported. node_modules is always implicitly excluded.

plugins: [
  [
    '@userpilot/babel-plugin-react-native-autocapture',
    {
      exclude: ['src/screens/internal/Debug.tsx', 'src/storybook/'],
    },
  ],
];

What Gets Emitted

Each instrumented interaction produces an InteractionEvent whose shape mirrors the iOS and Android Userpilot SDKs. The runtime SDK forwards it through autoCaptureEvent(eventType, payload) to the same analytics pipeline iOS and Android use — no schema branching downstream.

{
  raw_interaction_type: 'tap' | 'switch_changed' | 'text_field_changed' | 'view_presented' | …,
  target_class: 'react-native/Pressable',
  target_view_name: 'Pressable',
  target_text: 'Login',
  accessibility_label: 'Login button',
  accessibility_identifier: 'login-button',
  hierarchy: 'Pressable:attr__id="login-button",attr__index="0",attr__desc="Login button";View:attr__index="0";LoginScreen',
  // and more, depending on InteractionType — see the InteractionEvent reference.
}

⚙️ Coexistence with the SDK

@userpilot/react-native does not monkey-patch React Native components. Interaction autocapture depends on this Babel plugin wrapping source files that pass through Babel.

  • Source files that go through Metro/Babel are instrumented automatically once the plugin is configured.
  • Precompiled third-party libraries that do not go through the app's Babel pipeline are not instrumented unless you explicitly compile them through Metro/Babel and configure matching targets.
  • Calling Userpilot.stopAutocapture() at runtime suppresses event dispatch from already-wrapped components, so you don't need to disable the plugin to pause capture during sensitive flows.

⚠️ Limitations

  • Compile-time only. Code that doesn't go through Babel (precompiled native libraries, third-party bundles) is invisible to the instrumenter.
  • React.createElement(Pressable, …) direct calls are not wrapped. The plugin only sees JSX.
  • HOCs that capture a component reference (styled(Pressable)) are not wrapped at the call site. Add the wrapped HOC to targets instead.
  • WebView interactions are out of scope — content inside a WebView is web territory.

📝 Documentation

For the high-level autocapture story, including screen tracking adapters, privacy controls, and runtime APIs, see the React Native Autocapture guide.

For the runtime SDK reference, see @userpilot/react-native.

Full product documentation is available at Userpilot.

🎬 Examples

The example directory in this repository contains a full example iOS/Android app that uses this plugin alongside @userpilot/react-native for end-to-end autocapture, including third-party targets, privacy components, and screen tracking via React Navigation.

📄 License

This project is licensed under the MIT License. See LICENSE for more information.