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

@bugcapture/react-native

v0.2.1

Published

BugCapture for React Native — one-tap bug reports with screenshot, logs and device context, filed to the same backend as the browser extension.

Readme

@bugcapture/react-native

BugCapture for React Native. A tester taps a button, types a title and description, hits Save — and a bug report with a screenshot and full device context lands in the same dashboard the browser extension writes to, ready to file to Jira, ClickUp or GitHub.

This is M1. It writes a real report end to end. The queue (M2), console/network capture (M3) and touch trails (M4) come next — see docs/FEATURE_mobile_sdk.md for the full milestone list.


⚠️ Run the M0 spike before integrating

The SDK's single biggest technical risk is react-native-view-shot on your RN version and architecture. If it doesn't work on Fabric/New Architecture, the screenshot approach has to change — and everything else here is fine but far less useful. Find out first; it takes ten minutes.

import { captureScreen } from "react-native-view-shot";

<Button title="spike" onPress={async () => {
  const uri = await captureScreen({ format: "jpg", quality: 0.6, result: "tmpfile", width: 1080 });
  console.log("captured:", uri);   // open it — is it the screen, or black?
}} />

Check on a real device, not just the simulator, and specifically on a screen with whatever your app uses (camera, maps, video, WebView). Also confirm react-native-keychain survives an app restart.

If the screenshot comes back black or throws, stop and tell me — only src/capture/screenshot.ts needs to change, but it needs to change before M2.


Install

npm install @bugcapture/react-native \
  react-native-view-shot react-native-device-info react-native-keychain \
  react-native-blob-util @react-native-community/netinfo
cd ios && pod install

react-native-blob-util and @react-native-community/netinfo are optional but recommended: blob-util streams the screenshot upload instead of buffering it as base64 (which matters against the 5 MB cap), and netinfo records the connection type. The SDK degrades quietly without either.

Not compatible with Expo Go — it needs a dev client or prebuild.

Upgrading to 0.2.0 needs a native rebuild

0.2.0 ships an android/ folder, so the package is now autolinked. Rebuild after upgrading — a Metro reload will not pick it up:

npx expo run:android      # or: cd android && ./gradlew assembleDebug

Skip it and nothing breaks: every native path degrades to what 0.1.8 did. You simply do not get shake-to-report or the all-window screenshot. There is no iOS native module yet, so pod install has nothing new to do.

Integrate

One line at the app entry:

// index.js
import { withBugCapture } from "@bugcapture/react-native";
import App from "./App";

export default withBugCapture(App, {
  firebase: {
    apiKey: "…",
    projectId: "bugcapture-qa",
    storageBucket: "bugcapture-qa.firebasestorage.app",
  },
  environment: "staging",
});

Then trigger it from the button next to your existing log button:

import { BugCapture } from "@bugcapture/react-native";

<Pressable onPress={() => BugCapture.show()}>
  <BugIcon />
</Pressable>

Optionally, tell it which screen the tester is on so reports carry a route instead of an empty URL:

<NavigationContainer onStateChange={BugCapture.onNavigationStateChange}>

How testers sign in

Once per install, with the same email and password as the BugCapture dashboard. The refresh token is stored in the Keychain, so they never see the sign-in sheet again unless they sign out.

There is no sign-up in the app — accounts are created on the dashboard. This is deliberate: signing in as themselves is what makes reports appear under their name and what lets Jira/ClickUp filing use their saved integration credentials.

API

| Call | Purpose | | --- | --- | | withBugCapture(App, config) | Wrap the root component | | <BugCaptureOverlay> | Wrap your own <Modal>'s contents so the button — and the screenshot — reach inside that window | | BugCapture.show({ route? }) | Open the bug sheet (captures the screen first) | | BugCapture.hide() | Close it | | BugCapture.setRoute(name) | Set the current screen manually | | BugCapture.onNavigationStateChange | Spread onto NavigationContainer | | BugCapture.setFloatingButton(on) | Show/hide the draggable bug button | | BugCapture.setFloatingButtonOpacity(v) | Fade it, 0.21 | | BugCapture.log(message, level?) | Add a line from your own logger | | BugCapture.addStep(label) | Add a step to the reproduction trail | | BugCapture.signIn/signOut/getUser | Session control |

Config

| Key | Default | | | --- | --- | --- | | firebase | — | { apiKey, projectId, storageBucket } (required) | | orgId | — | Which workspace reports go to. Only needed when a tester belongs to more than one — otherwise the sheet asks, or uses the only one | | environment | "UAT" | Preselects an Environment chip. Dev/UAT/Prod match case-insensitively; anything else is added as a fourth chip | | floatingButton | false | Start with the draggable bug button visible | | floatingButtonOpacity | 1 | 0.21; lets a tester fade it over a screen it covers | | captureConsole | true | console.*, uncaught errors, unhandled rejections | | captureNetwork | true | fetch / axios / XHR / WebSocket | | captureNetworkBodies | false | Request/response bodies. Off by default — redaction catches tokens, not customer_name | | logWindowMinutes | 5 | How much history a report carries. 0 = count caps only | | screenshotWidth | 1080 | Longest edge before JPEG encode | | captureAsyncStorage | false | Off by default — apps keep auth tokens there | | shakeToReport | true | Shake the phone to open the sheet. Android, and a no-op until you rebuild. Turn it off for a build that goes to real users | | shakeSensitivity | 1 | 0.23; higher is more sensitive. Clamped so it can never be set into walking range | | nativeCapture | true | Use the all-window native screenshot when it is available. false forces the JavaScript path | | extra | {} | Key/values attached to every report |

Shake to report

Shake the phone on any screen and the sheet opens. It is the only trigger that reaches a native screen, a system dialog or a <Modal> nobody wrapped — a view-based trigger cannot leave its own window.

On by default, unlike the floating button: a button occupies your UI from the moment it installs, a shake occupies nothing until someone shakes. Set shakeToReport: false for a build that goes to real users, where a dropped phone should not open a QA form.

Needs the native module, so it does nothing until you rebuild. Android only for now.

Marking up the screenshot

Tap the thumbnail on the sheet and the capture opens full screen: Select · Box · Arrow · Pixelate · Hide · Step, four colours, undo, redo and clear. Pinch to zoom, double-tap to toggle, one finger draws.

Two ways to hide something, and they answer different questions. Pixelate is a brush that paints mosaic blocks — it keeps the shape of what was there, useful when the reader needs to know a name was present without reading it. Hide paints an opaque bar and leaves nothing of the original; that is the one for card numbers and OTPs. Blurs have been read back, and a QA screenshot pasted into a ticket is exactly where a half-removed phone number matters.

The marks are burned into the uploaded JPEG, so what reaches Jira is already redacted. The shape list also rides on report.json under annotations, in normalised 0..1 coordinates, so the annotation stays editable. Only the flattened image is ever uploaded — the raw one would put the redaction one download away from undone.

Marks stay editable. With Select active, tap a mark to pick it, drag to move it, drag a corner or an arrow end to resize it, tap a colour to recolour it, Delete to remove it. Undo and Redo cover all of it.

No freehand pen: it is the one tool that needs a real drawing surface, and it was not worth adding a rendering dependency to this package for.

What the sheet asks for

Title, Description, Create in, Environment (Dev/UAT/Prod), Severity (Blocker–Trivial), Priority (P1–P4) — the same fields and the same defaults as the browser extension, so one bug looks the same wherever it was filed.

Three more appear only when they mean something: Workspace when the tester belongs to more than one and orgId is not set, Folder when the workspace has any, and Assignee when it has members.

The floating button

The SDK keeps no preference of its own — setFloatingButton(on) is a runtime switch and floatingButton is its starting state. Store the choice wherever your app stores settings and call it again on launch:

useEffect(() => {
  AsyncStorage.getItem("floatingBug").then((v) =>
    BugCapture.setFloatingButton(v === "1")
  );
}, []);

It hides itself while the screenshot is taken, so it never appears in a report.

Your own modals

A React Native <Modal> is a separate OS window — an Android Dialog, an iOS presented view controller. Nothing in your app's view tree can draw over one at any zIndex or elevation, and captureScreen() photographs the activity behind it rather than the modal itself. So the button disappears over a modal, and a screenshot taken there would be of the wrong screen.

Wrap the modal's contents and both problems go away:

import { BugCaptureOverlay } from "@bugcapture/react-native";

<Modal visible={open} transparent onRequestClose={close}>
  <BugCaptureOverlay>
    …your sheet…
  </BugCaptureOverlay>
</Modal>

Only one button exists at a time — nested modals hand it inward and back — and its position survives the handover. It is layout-neutral and safe to leave in even with the provider absent.

Do not wrap a payment or credential screen. The button being there invites a screenshot of a screen that should not be photographed, and redaction catches tokens, not card numbers.

This covers modals you own. Alert.alert, native share sheets and anything else the JS tree never sees are still out of reach — that needs a native trigger.

What this does NOT do

Stated up front so nobody is surprised in a bug triage:

  • It is not a crash reporter. A native crash (SIGSEGV, uncaught Java exception) kills the process before anything can be written. Use Crashlytics alongside it.
  • Screenshots miss camera, video, MapView and hardware-layer WebViews on the JavaScript path — those surfaces render black, because nothing can read them back from the view hierarchy. The native path (0.2.0, Android, after a rebuild) uses PixelCopy and does read them.
  • Screenshots do not include Alert.alert, native pickers, share sheets, system UI or other apps — even natively. Those are windows the app does not own; reaching them needs MediaProjection and its consent dialog before every report.
  • Native-side network is invisible. <Image> loads, analytics, payment and map SDKs all issue their requests natively, so they never cross the JS bridge and cannot appear in the network tab. Only fetch/axios/XHR and WebSocket traffic is captured. Seeing the rest needs a native interceptor (OkHttp / NSURLProtocol), which is a native module rather than a patch.
  • Console is empty in release builds if babel-plugin-transform-remove-console is enabled — the plugin deletes the calls before this SDK can see them. Use BugCapture.log() from your own logger if you strip console in release.
  • Individual taps are not recorded. A React Native touch carries a node tag, not a label, and the only route to one runs through React's internals. Screen changes, foreground/background, rotation, connectivity and iOS memory warnings are recorded automatically; use BugCapture.addStep() for the rest.
  • Native crashes are invisible, and so is logcat / NSLog output — both live below the JS runtime.
  • There is no mobile dashboard. No report list, history or editing in the app — reports are read and triaged at https://bugcapture-qa.web.app.

Development

npm test          # pure-logic tests: ids, redaction, report assembly
npm run typecheck # tsc --noEmit

The tests cover everything that can be verified without a device — ID collision resistance, PII redaction, and the report/summary contract with the backend. The capture and UI layers need the M0 spike and a real device.