by Isaías Chávez
A cross-platform React Native module for accessing the device proximity sensor using the New Architecture (JSI/TurboModules).
npm install react-native-device-proximityor
yarn add react-native-device-proximityFor iOS, install CocoaPods dependencies:
cd ios && pod installimport {
isProximityAvailable,
addProximityListener,
removeProximityListener,
validateSensor
} from 'react-native-device-proximity';
// Check if proximity sensor is available
const available = await isProximityAvailable();
console.log('Proximity sensor available:', available);
// Subscribe to proximity changes
const subscription = addProximityListener((data) => {
console.log('Near:', data.near);
console.log('Distance (Android only):', data.distance);
});
// Validate sensor functionality
const validation = await validateSensor(2000); // 2 second timeout
console.log('Sensor works:', validation.ok);
console.log('Reason:', validation.reason);
console.log('Samples received:', validation.samples);
// Unsubscribe when done
subscription.remove();
// or
removeProximityListener();isProximityAvailable(): Promise<boolean>Checks if the proximity sensor is available on the device.
Returns: Promise<boolean> - true if sensor is available, false otherwise.
Example:
const available = await isProximityAvailable();addProximityListener(callback)Subscribes to proximity sensor changes.
Parameters:
callback: Function called when proximity changesdata.near (boolean): true if object is near the sensordata.distance (number | null): Distance in cm (Android only, null on iOS)Returns: EmitterSubscription - Subscription object with .remove() method
Example:
const subscription = addProximityListener((data) => {
if (data.near) {
console.log('Object detected nearby!');
}
});
// Later...
subscription.remove();removeProximityListener(): voidRemoves all proximity listeners. Alternative to calling .remove() on the subscription.
Example:
removeProximityListener();validateSensor(timeoutMs): Promise<ValidationResult>Validates that the proximity sensor is functional.
Parameters:
timeoutMs (number): Timeout in milliseconds (Android only, ignored on iOS)Returns: Promise<ValidationResult>
ok (boolean): true if sensor is functionalreason (string): Reason code (see Platform Differences below)samples (number): Number of sensor events receivedExample:
const result = await validateSensor(2000);
if (result.ok) {
console.log('Sensor is working!');
} else {
console.log('Sensor issue:', result.reason);
}validateSensor BehaviortimeoutMs parameter to wait for sensor eventsSENSOR_DELAY_FASTEST for maximum samplingNO_EVENTS_IN_TIMEOUT: Sensor didn't respondTOO_FEW_EVENTS__DOUBTFUL: Only 1 event received (suspicious)EVENTS_RECEIVED: Multiple events received (sensor working)Example result:
{
ok: true,
reason: "EVENTS_RECEIVED",
samples: 47 // Received 47 events in 2 seconds
}// Android proximity logic
const near = distance < 5.0; // Object within 5cm thresholdKey Difference: iOS only notifies on state changes, not continuously like Android.
- ❌ Only events on state change:
UIDeviceProximityStateDidChangeNotificationonly fires when state changes from true → false or false → true- ❌ Few samples: In 2000ms you might receive 0, 1, or 2 events maximum (depending on user interaction)
- ❌ Validation not sample-based: Can't validate by counting samples - sensor only notifies on state changes, not continuously
- ❌ Binary only: Only states true/false, no distance values
validateSensor BehaviortimeoutMs parameter (validation is instant)proximityMonitoringEnabledSENSOR_AVAILABLE: Sensor can be enabledSENSOR_NOT_AVAILABLE: Sensor cannot be enabledExample result:
{
ok: true,
reason: "SENSOR_AVAILABLE",
samples: 1 // Always 1 if available on iOS
}// iOS proximity logic
const near = device.proximityState; // true/false only
const distance = null; // iOS doesn't provide distance| Feature | Android | iOS |
|---|---|---|
| Sensor Type | Continuous | Binary (event on state change) |
| Distance Value | ✅ Yes (0-5cm typically) | ❌ No (null) |
| Event Frequency | High (hundreds/sec) | Low (only on state change) |
validateSensor Duration | Uses timeoutMs | Instant (ignores timeoutMs) |
| Validation Method | Count events in timeout | Check if sensor enables |
Typical samples Value | 20-100+ | Always 1 (if available) |
| Thread Safety | Handler-based | Main thread dispatched |
AtomicInteger for listener countingdistance will always be null in the callback dataimport { addProximityListener } from 'react-native-device-proximity';
import { useState, useEffect } from 'react';
function ProximityScreenLock() {
const [isNear, setIsNear] = useState(false);
useEffect(() => {
const subscription = addProximityListener((data) => {
setIsNear(data.near);
if (data.near) {
// Turn off screen or pause video
console.log('Object nearby - locking screen');
} else {
// Turn on screen or resume video
console.log('Object moved away - unlocking screen');
}
});
return () => subscription.remove();
}, []);
return (
<View>
<Text>Proximity: {isNear ? 'NEAR' : 'FAR'}</Text>
</View>
);
}See the contributing guide to learn how to contribute to the repository and the development workflow.
MIT
Made with create-react-native-library