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

@zeey4d/react-native-sensify

v1.0.0

Published

Unified, declarative React hooks for mobile sensors and gestures in React Native. Supports Expo and bare RN.

Readme

react-native-sensify 🎯

Unified, declarative React hooks for mobile sensors and gestures in React Native.

Simplify sensor data and gesture recognition with clean, TypeScript-first hooks that work across Expo Managed, Expo Bare, and React Native CLI projects.

npm license TypeScript


✨ Features

  • 🎯 15 production-ready hooks — sensors, gestures, and smart combinations
  • 📱 Cross-platform — iOS + Android
  • 🏗️ Works everywhere — Expo Managed, Expo Bare, and bare React Native CLI
  • 🔒 TypeScript-first — Full type safety with exported interfaces
  • Performant — Reanimated worklets run on the UI thread at 60fps
  • 🛡️ Error-safe — Graceful degradation when sensors aren't available
  • 🧹 Clean — Automatic cleanup on unmount, no memory leaks

📦 Installation

npm install react-native-sensify

Peer Dependencies

You must also install these peer dependencies:

# Expo projects
npx expo install expo-sensors react-native-gesture-handler react-native-reanimated

# Bare React Native CLI projects
npm install expo-sensors react-native-gesture-handler react-native-reanimated

Setup

  1. react-native-gesture-handler: Wrap your root component with <GestureHandlerRootView>:
import { GestureHandlerRootView } from 'react-native-gesture-handler';

export default function App() {
  return (
    <GestureHandlerRootView style={{ flex: 1 }}>
      {/* Your app */}
    </GestureHandlerRootView>
  );
}
  1. react-native-reanimated: Add the Babel plugin to your babel.config.js:
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['react-native-reanimated/plugin'],
};

🎮 Sensor Hooks

useAccelerometer

Access accelerometer data with built-in shake detection.

import { useAccelerometer } from 'react-native-sensify';

function AccelerometerDemo() {
  const { x, y, z, isShaking, shakeMagnitude, available } = useAccelerometer({
    interval: 100,       // Update every 100ms
    shakeThreshold: 1.8, // Adjust shake sensitivity
  });

  if (!available) return <Text>Accelerometer not available</Text>;

  return (
    <View>
      <Text>X: {x.toFixed(2)}</Text>
      <Text>Y: {y.toFixed(2)}</Text>
      <Text>Z: {z.toFixed(2)}</Text>
      <Text>Magnitude: {shakeMagnitude.toFixed(2)}</Text>
      {isShaking && <Text>🔥 SHAKING!</Text>}
    </View>
  );
}

useGyroscope

Access gyroscope rotation rates with automatic tilt direction.

import { useGyroscope } from 'react-native-sensify';

function GyroscopeDemo() {
  const { x, y, z, tiltDirection, available } = useGyroscope({
    interval: 100,
    tiltThreshold: 0.3,
  });

  return (
    <View>
      <Text>Rotation: {x.toFixed(2)}, {y.toFixed(2)}, {z.toFixed(2)}</Text>
      <Text>Tilt: {tiltDirection}</Text>
      {/* tiltDirection: 'left' | 'right' | 'forward' | 'back' | 'neutral' */}
    </View>
  );
}

useMagnetometer

Access magnetometer data with compass heading.

import { useMagnetometer } from 'react-native-sensify';

function CompassDemo() {
  const { compassHeading, available } = useMagnetometer({ interval: 200 });

  return (
    <View>
      <Text>Heading: {compassHeading}° from North</Text>
      <View style={{ transform: [{ rotate: `${360 - compassHeading}deg` }] }}>
        <Text>🧭 N</Text>
      </View>
    </View>
  );
}

useShake

Detect shake gestures with a simple callback.

import { useShake } from 'react-native-sensify';

function ShakeDemo() {
  useShake(
    () => {
      Alert.alert('Shake detected! 🎲');
    },
    {
      threshold: 1.5,  // G-force threshold
      timeout: 1000,   // Cooldown between shakes (ms)
    }
  );

  return <Text>Shake your phone!</Text>;
}

useTilt

Detect tilt direction with directional callbacks.

import { useTilt } from 'react-native-sensify';

function TiltDemo() {
  useTilt({
    onLeft: () => console.log('⬅️ Tilted left'),
    onRight: () => console.log('➡️ Tilted right'),
    onForward: () => console.log('⬆️ Tilted forward'),
    onBack: () => console.log('⬇️ Tilted back'),
    sensitivity: 0.4,   // 0.0 – 1.0
  });

  return <Text>Tilt your device!</Text>;
}

useDeviceOrientation

Detect device orientation with hysteresis.

import { useDeviceOrientation } from 'react-native-sensify';

function OrientationDemo() {
  const { orientation, available } = useDeviceOrientation();

  // orientation: 'portrait' | 'landscape-left' | 'landscape-right' | 'upside-down'
  return (
    <View style={{
      flexDirection: orientation.startsWith('landscape') ? 'row' : 'column',
    }}>
      <Text>Orientation: {orientation}</Text>
    </View>
  );
}

👆 Gesture Hooks

All gesture hooks return a gesture object. Wrap your content with <GestureDetector>:

import { GestureDetector } from 'react-native-gesture-handler';

<GestureDetector gesture={gesture}>
  <View>{/* your content */}</View>
</GestureDetector>

useSwipe

Detect 4-direction swipes with velocity thresholds.

import { useSwipe } from 'react-native-sensify';
import { GestureDetector } from 'react-native-gesture-handler';

function SwipeDemo() {
  const { gesture } = useSwipe({
    onLeft: () => console.log('← Swiped left'),
    onRight: () => console.log('→ Swiped right'),
    onUp: () => console.log('↑ Swiped up'),
    onDown: () => console.log('↓ Swiped down'),
    threshold: 50,          // Min distance (px)
    velocityThreshold: 500, // Min velocity
  });

  return (
    <GestureDetector gesture={gesture}>
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Swipe in any direction!</Text>
      </View>
    </GestureDetector>
  );
}

usePinch

Detect pinch-in and pinch-out gestures.

import { usePinch } from 'react-native-sensify';
import { GestureDetector } from 'react-native-gesture-handler';

function PinchDemo() {
  const { gesture, scale } = usePinch({
    onPinchIn: (s) => console.log('Pinching in:', s),
    onPinchOut: (s) => console.log('Pinching out:', s),
    onPinchEnd: (s) => console.log('Final scale:', s),
  });

  return (
    <GestureDetector gesture={gesture}>
      <View style={{ transform: [{ scale }] }}>
        <Text>Pinch me! 🤏</Text>
      </View>
    </GestureDetector>
  );
}

useLongPress

Detect long press gestures.

import { useLongPress } from 'react-native-sensify';
import { GestureDetector } from 'react-native-gesture-handler';

function LongPressDemo() {
  const { gesture } = useLongPress(
    () => Alert.alert('Long pressed! 🔴'),
    { duration: 800 } // 800ms hold
  );

  return (
    <GestureDetector gesture={gesture}>
      <View style={{ padding: 40, backgroundColor: '#e74c3c', borderRadius: 12 }}>
        <Text style={{ color: 'white' }}>Hold me down</Text>
      </View>
    </GestureDetector>
  );
}

useDoubleTap

Detect double tap gestures.

import { useDoubleTap } from 'react-native-sensify';
import { GestureDetector } from 'react-native-gesture-handler';

function DoubleTapDemo() {
  const { gesture } = useDoubleTap(() => {
    console.log('Double tapped! ❤️');
  });

  return (
    <GestureDetector gesture={gesture}>
      <View style={{ padding: 40 }}>
        <Text>Double tap me!</Text>
      </View>
    </GestureDetector>
  );
}

usePan

Track pan gestures with position and velocity.

import { usePan } from 'react-native-sensify';
import { GestureDetector } from 'react-native-gesture-handler';

function PanDemo() {
  const { gesture, panData } = usePan({
    onStart: (data) => console.log('Started:', data.x, data.y),
    onMove: (data) => console.log('Δ:', data.translationX, data.translationY),
    onEnd: (data) => console.log('Velocity:', data.velocityX, data.velocityY),
    minDistance: 10,
  });

  return (
    <GestureDetector gesture={gesture}>
      <View style={{ flex: 1 }}>
        <Text>Translation: {panData.translationX.toFixed(0)}, {panData.translationY.toFixed(0)}</Text>
        <Text>Velocity: {panData.velocityX.toFixed(0)}, {panData.velocityY.toFixed(0)}</Text>
      </View>
    </GestureDetector>
  );
}

useRotate

Detect rotation gestures with angle tracking.

import { useRotate } from 'react-native-sensify';
import { GestureDetector } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle } from 'react-native-reanimated';

function RotateDemo() {
  const { gesture, angle } = useRotate({
    onRotate: (a) => console.log('Angle:', a, 'rad'),
  });

  return (
    <GestureDetector gesture={gesture}>
      <Animated.View style={{ transform: [{ rotate: `${angle}rad` }] }}>
        <Text>🔄 Rotate me!</Text>
      </Animated.View>
    </GestureDetector>
  );
}

🧠 Combined / Smart Hooks

useTiltScroll

Tilt the device to scroll content.

import { useTiltScroll } from 'react-native-sensify';
import Animated, { useAnimatedStyle } from 'react-native-reanimated';

function TiltScrollDemo() {
  const { scrollY, tiltDirection, available } = useTiltScroll({
    sensitivity: 0.4,
    maxSpeed: 15,
  });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateY: -scrollY.value }],
  }));

  return (
    <Animated.View style={animatedStyle}>
      <Text>Tilt forward to scroll down, back to scroll up!</Text>
      {/* Your scrollable content */}
    </Animated.View>
  );
}

useShakeUndo

Shake to trigger undo with optional confirmation alert.

import { useShakeUndo } from 'react-native-sensify';

function UndoDemo() {
  const [actions, setActions] = useState<string[]>([]);

  useShakeUndo({
    onUndo: () => {
      setActions((prev) => prev.slice(0, -1));
    },
    showAlert: true,
    alertTitle: 'Undo Action',
    alertMessage: 'Do you want to undo the last action?',
    threshold: 1.5,
    timeout: 2000,
  });

  return <Text>Shake to undo! Actions: {actions.length}</Text>;
}

useMotionAnimation

Create motion-reactive UI elements driven by device accelerometer.

import { useMotionAnimation } from 'react-native-sensify';
import Animated from 'react-native-reanimated';

function FloatingCardDemo() {
  const { animatedStyle, available } = useMotionAnimation({
    scaleX: 50,     // 50px max X movement
    scaleY: 50,     // 50px max Y movement
    damping: 10,    // Spring damping
    stiffness: 100, // Spring stiffness
  });

  return (
    <Animated.View
      style={[
        {
          width: 200,
          height: 200,
          backgroundColor: '#6c5ce7',
          borderRadius: 20,
          justifyContent: 'center',
          alignItems: 'center',
        },
        animatedStyle,
      ]}
    >
      <Text style={{ color: 'white', fontSize: 18 }}>
        I move with the device! 🎭
      </Text>
    </Animated.View>
  );
}

🔧 Utilities

Permission Handling

import { checkSensorPermission, requestSensorPermission } from 'react-native-sensify';

const hasPermission = await checkSensorPermission();
const granted = await requestSensorPermission();

Throttle & Debounce

import { throttle, debounce } from 'react-native-sensify';

const throttled = throttle(() => console.log('throttled'), 200);
const debounced = debounce(() => console.log('debounced'), 300);

// Clean up
throttled.cancel();
debounced.cancel();

🧪 Testing Strategy

Unit Tests (Jest)

// Mock expo-sensors for testing
jest.mock('expo-sensors', () => ({
  Accelerometer: {
    isAvailableAsync: jest.fn().mockResolvedValue(true),
    setUpdateInterval: jest.fn(),
    addListener: jest.fn().mockReturnValue({ remove: jest.fn() }),
  },
  // ... same for Gyroscope, Magnetometer
}));

// Mock react-native-gesture-handler
jest.mock('react-native-gesture-handler', () => ({
  Gesture: {
    Pan: jest.fn().mockReturnValue({
      enabled: jest.fn().mockReturnThis(),
      onEnd: jest.fn().mockReturnThis(),
      onStart: jest.fn().mockReturnThis(),
      onUpdate: jest.fn().mockReturnThis(),
      minDistance: jest.fn().mockReturnThis(),
    }),
    // ... same for Pinch, Tap, LongPress, Rotation
  },
}));

Testing Utilities

npm install --save-dev jest @testing-library/react-native @testing-library/react-hooks

E2E Testing

For physical device testing, we recommend:

  • Maestro — YAML-based mobile UI testing
  • Detox — Gray-box E2E testing for React Native

🚀 Publishing to npm

1. Prepare

# Build the library
npm run build

# Verify the dist/ folder looks correct
ls dist/

2. Login to npm

npm login

3. Publish

# First publish
npm publish

# Subsequent versions
npm version patch  # or minor / major
npm publish

4. Versioning Strategy

Follow Semantic Versioning (SemVer):

| Change Type | Version Bump | Example | |--------------------------|-------------|---------------| | Bug fixes | patch | 1.0.0 → 1.0.1 | | New hooks/features | minor | 1.0.0 → 1.1.0 | | Breaking API changes | major | 1.0.0 → 2.0.0 |


📋 API Reference

Sensor Hooks

| Hook | Returns | |-------------------------|------------------------------------------------------------| | useAccelerometer | { x, y, z, isShaking, shakeMagnitude, available } | | useGyroscope | { x, y, z, tiltDirection, available } | | useMagnetometer | { x, y, z, compassHeading, available } | | useShake | void (fires callback) | | useTilt | void (fires directional callbacks) | | useDeviceOrientation | { orientation, available } |

Gesture Hooks

| Hook | Returns | |------------------|------------------------------------------------------------------| | useSwipe | { gesture } | | usePinch | { gesture, scale } | | useLongPress | { gesture } | | useDoubleTap | { gesture } | | usePan | { gesture, panData: { x, y, translationX, translationY, ... }} | | useRotate | { gesture, angle } |

Combined Hooks

| Hook | Returns | |-----------------------|------------------------------------------------| | useTiltScroll | { scrollY (SharedValue), tiltDirection, available } | | useShakeUndo | void (fires onUndo callback) | | useMotionAnimation | { animX, animY, animZ, animatedStyle, available } |


🤝 Contributing

  1. Fork the repository
  2. Create your feature branch: git checkout -b feature/amazing-hook
  3. Commit your changes: git commit -m 'Add amazing hook'
  4. Push to the branch: git push origin feature/amazing-hook
  5. Open a Pull Request

📄 License

MIT © [Your Name]


Made with ❤️ for the React Native community