@loupfeed/react-native
v0.2.0
Published
loupfeed React Native adapter — init() + <FeedbackRoot> native inspector overlay (testID threading + measureInWindow hit-testing) + useFeedback(). Pairs with the @loupfeed/babel-plugin fbId Metro tagger.
Downloads
250
Readme
@loupfeed/react-native
React Native adapter with a native inspector overlay — built and verified on the iOS simulator (Expo SDK 52, New Architecture / bridgeless).
RN has no DOM, so the opaque id rides as an fbId prop threaded to the native
view's testID, and the inspector hit-tests touches via measureInWindow (the
native analogue of the web overlay's elementsFromPoint). Same opaque-id +
server-manifest model as web — different carrier.
How it works
- Build-time tagger (Metro/Babel):
@loupfeed/babel-pluginwith{ attribute: 'fbId' }injects an opaquefbIdprop on every JSX element. No source paths reach the app bundle — theid → src:linemanifest is server-side. - Runtime: this adapter patches
React.createElementto (a) threadfbId→testID/accessibilityLabeland (b) register a measurable ref.<FeedbackRoot>renders a launcher + an inspect capture layer; a tap is hit-tested (measureInWindow) against the registered views, the nearestfbIdis read, a highlight is drawn, and a commentModalsubmits viacaptureFeedback. - Uses only core RN APIs (no custom native module) → runs in Expo Go.
Usage
import { init, FeedbackRoot, useFeedback } from '@loupfeed/react-native';
init({ dsn });
export default () => (
<FeedbackRoot>
<App />
</FeedbackRoot>
);// babel.config.js — the fbId Metro tagger + JSX routing. On the automatic JSX
// runtime (React 17+/19 default) set jsxImportSource so tagged elements thread
// fbId without `import React`; the classic createElement patch is also supported.
module.exports = (api) => {
api.cache(true);
return {
presets: [['babel-preset-expo', { jsxImportSource: '@loupfeed/react-native' }]],
plugins: [['@loupfeed/babel-plugin', { attribute: 'fbId' }]],
};
};A complete, runnable Expo example (Metro monorepo config + self-test) is in
examples/react-native.
Session replay (optional)
Attach a replay of the moments leading up to each report by registering a recorder. Two engines:
Screenshot replay (real pixels) — recommended. Samples actual screenshots (~3 fps, rolling ~12 s window) so the reviewer sees icons, images, input contents and backgrounds — video-like playback.
Requires the peer dependency
react-native-view-shot— a native module, so it needspod installon iOS / a native rebuild on Android and is not available in Expo Go:npx expo install react-native-view-shot # or: npm i react-native-view-shot && pod installimport { setReplayRecorder } from '@loupfeed/react-native'; import { screenshotRecorder } from '@loupfeed/react-native/screenshot'; init({ dsn, replaysSampleRate: 1.0 }); setReplayRecorder(screenshotRecorder);Wireframe replay (no native dep). Pure-JS view-hierarchy snapshots (boxes + optionally-masked text). Runs in Expo Go, but shows structure only, not pixels.
import { setReplayRecorder, wireframeRecorder, configureWireframeRecorder } from '@loupfeed/react-native'; setReplayRecorder(wireframeRecorder); configureWireframeRecorder({ maskText: false }); // show real text (default: masked)
Capture is a short rolling buffer kept in memory and continuously discarded — nothing is uploaded during normal use. Only when a user submits feedback is the ~12 s window frozen and uploaded in the background (non-blocking). The tunables (fps, window length, JPEG quality) are constants in the screenshot recorder.
Verified
On the iPhone 16 simulator (Expo Go, SDK 52, bridgeless): the fbId tagger
registered 26+ native views; an inspect tap resolved via measureInWindow to the
opaque id fb93de5eb7f7, and captureFeedback produced an enriched event
(tag: Text, user: jdoe).
Peer dependencies
react,react-native— required.react-native-view-shot— optional, only needed for the screenshot replay recorder (see Session replay). The wireframe recorder and the inspector/feedback flow have no native dependency.
